Skip to main content

A collection of different modules for Python that make life a little bit easier.

Project description

Dot Dictionary

Makes a Python dictionary that can access keys with dot notation in addition to brackets ( []).

class DotDict(dict):

A Python dictionary that allows you to access keys in dot notation style.

Example:

mycar = DotDict(
    year = 2023,
    make = 'Dodge',
    model = 'Challenger',
    trims = DotDict(
        sport = DotDict(horsepower=256),
        rt = DotDict(horsepower=425),
        demon = DotDict(horsepower=712),
    )
)


 print(mycar.year, mycar['model'], 
         mycar.trims.rt.horsepower, 'hp')

Output: 2023 Challenger 425 hp

Attributes can be accessed by dot notation, as well as traditional bracket notation. New attributes can only be added via bracket notation. This essentially creates a dictionary that works like a JavaScript Object.

Grid Data Structure

A data structure to manipulate and traverse a 2-dimensional array in an intutive manner.

class Grid:

Attributes:

  • width: the width of the grid
  • height: the height of the grid
  • focus_xy: a tuple of coordinates within the grid data indicating the cell which is currently in focus
  • focus_obj: the data within the cell which is currently focused
  • grid: the raw 2-D array which stores the data

def __init__(self, width = None, height = None, default = None, data = None):

Parameters:

  • width: the width of the grid
  • height: the height of the grid
  • default: a default value to insert into empty cells
  • data: a 2-D array of data to populate the new grid

A Gridobject is a rectangluar 2-dimensional array, meaning that each row has the same number of columns. The size of this structure can be determined either by the provided widthand height, or by the datapassed into it. The widthand heightmust be specified unless datahas been provided. If datais provided without widthor height,the dimensions of the datawill be used as the size of the Gridobject.

NOTE: The datais assumed to be rectangular as well, and the height and width are determined by the first row and first column, respectively. If any rows below the first row are longer, they will be truncated and that data will not be inserted into the Gridobject.

If both dataand the widthor heightis provided, the appropriate dimension of the grid will be sized according to that value. If the value is greater than the dimension of the data, the defaultvalue will be inserted into those cells. If the value is less than the dimension of the data, then those cells will not be copied to the Gridobject. (See the populate()method below for more details)

def populate(self, data):

Parameters:

  • data: a 2-D array of data to populate the grid

This method fills in the Gridobject with the contents of each cell of the provided dataobject. The insertion to each cell of the Gridobject is a simple assignment. This means that literal values will be copies, but Python objects will be references.

NOTE: Because Python always does pass by reference, the assignment which takes place in populate()places a reference to any Python object which is in the cell of data. If you wish to have shallow or deep copies in the Gridobject, you must do those before passing the datato this method.

def check_bounds(self, value, upper, lower, raise_exc=True):

Parameters:

  • value: the value to check
  • upper: the maximum threshold of value
  • lower: the minimum threshold of value
  • raise_exc: a boolean indicating if an IndexErrorshould be raised when valueexceeds the upperor lowerbounds

Returns: The value,possibly adjusted by the bounds, or raise an IndexError

Usually, this method simply returns the unaltered value,unless it exceeds one or both of the bounds provided ( upperand lower). However, if raise_excis set to False, then this method will adjust the valueto stay within the bounds. If the valueexceeds one of the bounds, the method returns the bound itself, since it is the furthest value allowed. This can be useful in situations where you implicitly need the value to stay within the bounds without raising an error.

def focus(self, x=None, y=None, stay_in_bounds=False):

Parameters:

  • x: the horizontal coordinate to focus on in the grid
  • y: the vertical coordinate to focus on in the grid
  • stay_in_bounds: a boolean indicating if this method should automatically correct the focus coordinates if the provided xor yvalues are outside the bounds of the grid

Returns: The content of the cell at ( x, y) in the grid.

A major concept of the Gridis focus, also referred to in some other data structures and data mangement utilities as a "cursor". The focus of the Gridis the specific cell which can be read or manipulated. This method will use the provided xand/or yvalues to set the focus of the Gridto a specific cell. The coordinate system in use with a Gridobject is such that the cell in the top left corner is coordinate (0, 0). and values along the x and y axes are positive integer values. There are no decimal or negative values in a Gridobject's coordinates. If the provided xor yvalues are not within the bounds of the grid, an IndexErrorwill be raised, unless stay_in_boundsis set to True(see below).

If an xor yvalue is not provided, this method will use the current focus xor ycoordinate. Calling focus(), with no arguments, simply retuns the currently focused cell contents. Calling focus(x=2)would move the focus to the third column of the currently focused row.

NOTE: Keep in mind that the Gridcoordinate system starts at (0,0), so the first column is an x-coordinate of 0, not 1.

If stay_in_boundsis set to True, then this method implicitly limits the focus to the bounds of the grid. Any value outside the bounds, will be automatically corrected to the bound(s) that were crossed. This is useful for implementations that may attempt to exceed the grid bounds, but don't necessarily need to raise an exception when it occurs.

def focus_up(self, amount=1, stay_in_bounds=False):

Parameters:

  • amount: the number of cells to shift the focus upwards
  • stay_in_bounds: a boolean indicating if this method should automatically correct the focus coordinates if it is outside the bounds of the grid

Returns: The content of the cell that is amountnumber of cells above the currently focused cell.

This a convenience method which shifts the focus of the Gridobject upwards by an amount.It directly calls the focus()method (see above).

def focus_down(self, amount=1, stay_in_bounds=False):

Parameters:

  • amount: the number of cells to shift the focus downwards
  • stay_in_bounds: a boolean indicating if this method should automatically correct the focus coordinates if it is outside the bounds of the grid

Returns: The content of the cell that is amountnumber of cells below the currently focused cell.

This a convenience method which shifts the focus of the Gridobject downwards by an amount.It directly calls the focus()method (see above).

def focus_left(self, amount=1, stay_in_bounds=False):

Parameters:

  • amount: the number of cells to shift the focus to the left
  • stay_in_bounds: a boolean indicating if this method should automatically correct the focus coordinates if it is outside the bounds of the grid

Returns: The content of the cell that is amountnumber of cells to the left of the currently focused cell.

This a convenience method which shifts the focus of the Gridobject to the left by an amount.It directly calls the focus()method (see above).

def focus_right(self, amount=1, stay_in_bounds=False):

Parameters:

  • amount: the number of cells to shift the focus to the right
  • stay_in_bounds: a boolean indicating if this method should automatically correct the focus coordinates if it is outside the bounds of the grid

Returns: The content of the cell that is amountnumber of cells to the right of the currently focused cell.

This a convenience method which shifts the focus of the Gridobject to the right by an amount.It directly calls the focus()method (see above).

def typewriter_traverse(self):

Returns: a TypewriterGridIteratorfor use in a forloop

def serpentine_traverse(self):

Returns: a SerpentineGridIteratorfor use in a forloop

def vertical_typewriter_traverse(self):

Returns: a VerticalTypewriterGridIteratorfor use in a forloop

def vertical_serpentine_traverse(self):

Returns: a VerticalSerpentineGridIteratorfor use in a forloop

def spiral_in_traverse(self):

Returns: a SpiralInGridIteratorfor use in a forloop

class AbstractGridIterator:

An abstract class for deriving iterators for a Gridobject.

GridIteratorclasses are unique in that they don't simply return the content of the cell, they also return the x and y coordinates of the cell. When using a GridIterator, you must account for these extra values:

mygrid = Grid(24, 12)
for x, y, item in mygrid.typwriter_traverse():
   # do stuff

class TypewriterGridIterator(AbstractGridIterator):

"Typewriter" traversal is the most common way to traverse a 2-dimensional array. The iterator starts at (0, 0), and moves along the x-axis. When it reaches the end of the row, it goes down to the next row, and begins again at the 0 x-coordinate and travels along the x-axis.

Example:

1234
5678    typwriter
9000    traversal = 123456789000

class SeprentineGridIterator(AbstractGridIterator):

"Serpentine" traversal is another fairly common traversal method. The iterator starts at (0, 0), and moves along the x-axis, just like a typewriter traversal. However, when it reaches the end of the row, it simply changes direction after moving down a row.

Example:

1234
5678    serpentine
9000    traversal = 123487659000

class VerticalTypewriterGridIterator(AbstractGridIterator):

"Vertical Typewriter" traversal is the same as typewriter, except turned vertically. Starting at (0, 0), the iterator moves down the column. When it reaches the end of the column, it goes to the next one and, starting again at the 0 y-coordinate, traverses down teh column.

Example:

1234    vertical
5678    typewriter
9000    traversal = 159260370480

class VerticalSeprentineGridIterator(AbstractGridIterator):

"Vertical Serpentine" traversal is the same as serpentine, except turned vertically. Starting at (0, 0), the iterator moves down the column. When it reaches the end of the column, it changes direction, and traverses up the next column.

Example:

1234    vertical
5678    serpentine
9000    traversal = 159062370084

class SpiralInGridIterator(AbstractGridIterator):

"Spiral In" traversal is concerned with visiting the outermost cells of the grid before visiting those closer to the center. The iterator starts at (0, 0), and moves along the x-axis. However, upon reaching the end of the x-axis, it moves down the column, then backwards across the bottom row, and upwards towards the start. Upon reaching the start, the iterator moves down 1 row and does the same movement again, 1 "layer" deeper (and thus 1 unit closer to the center) than before. This continues until it reaches the center of the grid.

Example:

1234
5678    spiral in
9000    traversal = 123480009567

Indented Text File Reader

Utility for reading indented blocks of text from a file and forming a tree data structure for use in Python. This allows Python programs to use config files and data files which respect whitespace the same as Python itself.

class IndentReader:

Attributes:

  • filepath: path to the file this reads
  • preserve_newlines: boolean describing if the reader keeps newlines in the node data
  • allowed_indents: list of strings which are considered valid indentations
  • indent_char: the character or string the reader has identified for use as indentation in the current file
  • current_line: the current line number the reader is parsing
  • root: the root node of the tree data structure generated by the reader

def __init__(self, filepath, preserve_newlines = False, allowed_indents = ' \t', root_data='<root>'):

Parameters:

  • filepath: path to the file this reads
  • preserve_newlines: boolean describing if the reader keeps newlines in the node data
  • allowed_indents: list of strings which are considered valid indentations
  • root_data: default dataplaced in the root node

When an IndentReaderobject is instantiated, it will immediately attempt to read and parse the file. Once IndentReaderidentifies a valid indent string (via the allowed_indentsvalue), all indents must use that same character or string for their indentation (just like Python, you cannot mix tabs and spaces for indentation).

The completed data structure is available for direct consumption via the rootattribute. In the event that an error occurs during parsing, the reader raises a ReaderError. The exception message indicates the error and line number. The rootdata structure, current_line, and indent_charremain after parsing is complete in order to provide debug information, if necessary.

This class can be subclassed to create a specialized reader for specific file formats or use cases. Typically the only override needed is to change the parse_data()method to use logic that is specific to the use case desired.

def prepare_for_read(self):

Resets all variables to prepare to read a new file.

def read_file(self):

Reads and parses the file of filenameand stores the result in the rootattribute. This method is automatically called upon object instantiation.

def parse_line(self, line):

Parameters:

  • line: the raw line of text read from the file

Parses the line, identifies the indentation level, then creates a node and inserts it into the tree.

def find_node(self, indent):

Parameters:

  • indent: the indent string to match

Returns: The parent node which matches the indentprovided

Starting at the current node, this method traverses back up the parents to find the indentation level that matches, then returns the parent node of that indentation level. If no matching node is found, this raises a ReaderError.

def prepare_node(self, rawline, indent):

Parameters:

  • rawline: the raw line of text from the file
  • indent: the indentation string found on the line

Returns: a new Nodeobject with the indentand dataparsed from the rawline

This method calls the parse_data()method to assemble the data for the Nodeobject.

def parse_data(self, line, rawline):

Parameters:

  • line: the stripped line
  • rawline: the untouched raw text from the file

Returns: Any valid Python data to store in the dataof a Nodeobject.

This method should be overridden in any subclasses to provide more specific logic to parse and prepare data read from the file. By default, this method just returns the linevalue.

def output(self, exclude_root=False):

Parameters:

  • exclude_root: do not include the rootnode in the output string

Returns: A prettified string of the data structure stored within root.

class ReaderError(Exception):

A generic exception class for IndentReader.

class Node:

Attributes:

  • parent: the parent Nodeof this node
  • indent: the indentation string this node's datawas found at in the file
  • child_indent: the indentation string of this node's children
  • children: a list of child Nodeobjects
  • data: the data stored in this node

The tree data structure in the IndentReaderis formed by connecting Nodeobjects to each other as parents and children. Each node has 1 parent and 0 or more children.

def __init__(self, parent=None, indent='', data=''):

Parameters:

  • parent: the parent Nodeobject
  • indent: the indentation string this node's datawas found at in the file
  • data: the data stored in this node

def add_child(self, node):

Parameters:

  • node: the Nodeobject to add as a child

This method sets the child_indentattribute, adds the nodeto this node's children, and assigns this node as the parentor node.

def output(self, level=0, exclude_self=False):

Parameters:

  • level: inidcates how far the prettified data should be indented
  • exclude_self: do not include the datain the output string

Returns: A prettified string of the dataand childrenof this node

Indexed Object

A chimera of data structures, this object is an ordered, indexed hashtable with the ability to access attributes by both dot notation and bracket notation.

class IndexedObject():

This object provides the benefits of several data structures simultaneously.

Attribute Access

  • Dot notation - myobj.attr
  • Bracket notation (key) - myobj['attr']
  • Bracket notation (index) - myobj[2]
  • Built-in getattr()function - getattr(myobj,'attr', None)
  • Instance get()method - myobj.get('attr',None)
  • Can use len()to get number of attributes

Attribute Storage

  • Attributes are ordered
  • Attributes can change order
  • New attributes can be inserted at any index
  • Keys, values, and key-value pairs can be iterated in order
  • Can handle both keyed and keyless data in the same object

def __init__(self, *args, **kwargs):

Creates a new IndexedObject. This constructor creates the attributes differently based on the argsor kwargssupplied to it. The constructor cannot accept both argsand kwargstogether. The object must be instantiated with either only args,or only kwargs.All attributes will be inserted into the object in the order they are passed to the constructor.

If argvalues are tuples, it is assumed they are (key, value) tuples, and will assign attributes as such. Any other argvalues are considered keyless values, and will have an automatically generated key assigned to them. kwargsare inserted exactly they are passed, with the attribute name being the kwargname, and the value being the kwargvalue. Because the argscan be read as either key-value tuples, or keyless values, you can get around the arg& kwarglimitation by inserting keyword args as key-value tuples instead.

Keyless data is always given an automatically generated key anytime it is inserted into this object. Generated keys follow the pattern of _x_where xis an auto-incrementing counter that starts at zero. The increment is based on the lifetime of the object. For example, an IndexedObjectwith 8 keyless attributes has a previously generated key of _7_. If a new keyless attribute is added, this will increment to _8_. However, if a keyless attribute is removed, the generated key does not decrement.

NOTE: Objects which will frequently change the number of keyless attributes should not use IndexedObject, since there is a possibility of encountering the upper bounds of Python's built in inttype (typically 2,147,483,647 for most Python interpreters).

def get(self, key, default=None):

Parameters:

  • key: the attribute name to retrieve
  • default: the value to return if the keyis not found

Returns: The attribute indentified by key, or the defaultvalue if the key is not found

def keys(self):

Returns: A list of the keys in this object, in order.

def values(self):

Returns: A list of the values in this object, in order.

def items(self):

Returns: A list of the (key, value) tuples in this object, in order.

def index(self, key):

Parameters:

  • key: the key to search for

Returns: The index of a given key.

def insert(self, index, arg1, arg2=None):

Parameters:

  • index: the index to insert the attribute

  • arg1: either the value, or the key to insert

  • arg2: the value to insert

  • To insert keyless data: myobj.insert(2, value)

  • To insert keyed data: myobj.insert(2, key, value)

def append(self, arg1, arg2=None):

Parameters:

  • arg1: either the value, or the key to append

  • arg2: the value to append

  • To append keyless data: myobj.append(value)

  • To append keyed data: myobj.append(key, value)

def popindex(self, index):

Parameters:

  • index: index of the attribute to remove

Returns: A tuple of the key and value at the index

def pop(self, key=None):

Parameters:

  • key: key of the attribute to remove

Returns: A tuple of the index and value at the key

def extend(self, new_obj):

Parameters:

  • new_obj: a list, dict, or IndexedObjectto append to this object

Appends all of the attributes of new_objto this object. The attributes will be appended in order with the same keys. If duplicate keys are detected, a KeyErroris raised. When new_objis a list, each value will have a key auto-generated before being appended.

NOTE: Because duplicate keys are not allowed, using extend()with an IndexedObjectthat has keyless attributes will most likely cause a KeyErrorto be raised.

def reverse(self):

Reverses the order attributes in place.

def copy(self):

Returns: A shallow copy of this object.

def clear(self):

Removes all attributes from this object and resets the generated key to zero.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cerrax_py-1.0.0.tar.gz (20.9 kB view details)

Uploaded Source

Built Distribution

cerrax_py-1.0.0-py3-none-any.whl (18.7 kB view details)

Uploaded Python 3

File details

Details for the file cerrax_py-1.0.0.tar.gz.

File metadata

  • Download URL: cerrax_py-1.0.0.tar.gz
  • Upload date:
  • Size: 20.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.9.0 Darwin/21.6.0

File hashes

Hashes for cerrax_py-1.0.0.tar.gz
Algorithm Hash digest
SHA256 5eee21bd66ee005f8b94e4bbd86982807769901bcbe044bc740451b9123eb489
MD5 a0eb7cbe39c3d21373a1d79a5c0defff
BLAKE2b-256 b0dd4a14588c1f8e94c3bea8a9c710a4ea480805d719d43639ef439847e6c7dc

See more details on using hashes here.

File details

Details for the file cerrax_py-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: cerrax_py-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 18.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.9.0 Darwin/21.6.0

File hashes

Hashes for cerrax_py-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e3aa7aaea6697a9f73a43db217ab8d7b32ef6168bc5b20d1c0fff2c9a50d923e
MD5 861651cd920390b133572081ea41098e
BLAKE2b-256 d9999cb776a89864d635c40c50c3be76288b117b4fb6b1e80bc0d1a2d7a17b07

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page