Package: 0xf0f-codenode
ContentsWhat is this?How do I install it?How do I use it?ExtensionsReferenceWhat is this?The goal of this module is to help write code that generates code.
Focus is placed on enabling the user to easily describe,
build and reason about code structures rapidly.How do I install it?From PyPI:pip install 0xf0f-codenodeFrom GitHub:pip install git+https://github.com/0xf0f/codenodeHow do I use it?Like thejsonandpicklemodules,dumpanddumpsare used to generate output.
Code can be built using any tree of iterables containing strings,
indentation nodes and newline nodes.For example, the built-inlinefunction returns a tuple:fromcodenodeimportindentation,newlinedefline(content):returnindentation,content,newlineWhich we can combine withindentanddedentnodes:fromcodenodeimportline,indent,dedent,dumpsdefcounting_function(count_from,count_to):return[line(f'def count_from_{count_from}_to_{count_to}():'),indent,[line(f'print({i})')foriinrange(count_from,count_to)],dedent,]print(dumps(counting_function(0,5)))Which outputs:def count_from_0_to_5():
    print(0)
    print(1)
    print(2)
    print(3)
    print(4)But what if we want to count to a really big number, like
1,000,000,000,000,000?
It would be inefficient to store all those lines in memory
at once. We can use a generator to break them down into
individual parts instead:fromcodenodeimportindent,dedent,newline,indentation,dumpdefcounting_function_generator(count_from,count_to):yieldindentationyield'def count_from_',str(count_from),'_to_',str(count_to),'():'yieldnewlineyieldindentforiinrange(count_from,count_to):yieldindentation,'print(',str(i),')',newlineyielddedentwithopen('code.py','w')asfile:dump(counting_function_generator(0,1_000_000_000_000_000),file)We can also build a class with an__iter__method:fromcodenodeimportline,indent,dedent,dumpclassCountingFunction:def__init__(self,count_from,count_to):self.count_from=count_fromself.count_to=count_todef__iter__(self):yieldline(f'def count_from_{self.count_from}_to_{self.count_to}():')yieldindentforiinrange(self.count_from,self.count_to):yieldline(f'print({i})')yielddedentwithopen('code.py','w')asfile:dump(CountingFunction(0,1_000_000),file)Or a more generalized function class:classFunction:def__init__(self,name,*args):self.name=nameself.args=argsself.children=[]def__iter__(self):arg_string=', '.join(self.args)yieldline(f'def{self.name}({arg_string}):')yieldindentyieldself.childrenyielddedentclassCountingFunction(Function):def__init__(self,count_from,count_to):super().__init__(f'count_from_{count_from}_to_{count_to}')foriinrange(count_from,count_to):self.children.append(line(f'print({i})'))Leveraging python's iteration protocol like this allows:Mixing and matching whatever fits the use case to maximize tradeoffs,
such as using generators for their memory efficiency,
custom iterable classes for their semantics, or plain old lists and
tuples for their simplicity.Taking advantage of existing modules that offer tooling for
iterables, such as itertools.Building higher level structures from as many iterable building blocks
as desired.ExtensionsModule behaviour can be extended by overriding methods of thecodenode.writer.Writerandcodenode.writer.WriterStackclasses. An
example of this can be seen in thecodenode.debug.debug_patchfunction. The variablecodenode.default_writer_typecan be used to
replace theWritertype used indumpanddumpswith a custom one.Some modules with helper classes and functions are also provided:codenode_utilitiescontains general language agnostic helper functions and classesReferenceNoteThis section of the readme was generated using codenode itself.See docs/generate_readme.pyContentscodenode.dumpcodenode.dumpscodenode.linecodenode.indentcodenode.dedentcodenode.newlinecodenode.indentationcodenode.linescodenode.empty_linescodenode.indentedcodenode.default_writer_typecodenode.writer.Writercodenode.writer.WriterStackcodenode.nodes.newline.Newlinecodenode.nodes.depth_change.DepthChangecodenode.nodes.depth_change.RelativeDepthChangecodenode.nodes.depth_change.AbsoluteDepthChangecodenode.nodes.indentation.Indentationcodenode.nodes.indentation.RelativeIndentationcodenode.nodes.indentation.AbsoluteIndentationcodenode.nodes.indentation.CurrentIndentationcodenode.debug.debug_patchcodenode.dumpdefdump(node,stream,*,indentation='    ',newline='\n',depth=0,debug=False):...Process and write out a node tree to a stream.Parametersnode:Base node of node tree.stream:An object with a 'write' method.indentation:String used for indents in the output.newline:String used for newlines in the output.depth:Base depth (i.e. number of indents) to start at.debug:If True, will print out extra info when an error
occurs to give a better idea of which node caused it.codenode.dumpsdefdumps(node,*,indentation='    ',newline='\n',depth=0,debug=False)->str:...Process and write out a node tree as a string.Parametersnode:Base node of node tree.indentation:String used for indents in the output.newline:String used for newlines in the output.depth:Base depth (i.e. number of indents) to start at.debug:If True, will print out extra info when an error
occurs to give a better idea of which node caused it.ReturnsString representation of node tree.codenode.linedefline(content:'T')->'tuple[Indentation, T, Newline]':...Convenience function that returns a tuple containing
an indentation node, line content and a newline node.Parameterscontent:content of lineReturnstuple containing an indentation node, line content and
a newline node.codenode.indentA node representing a single increase in indentation level.codenode.dedentA node representing a single decrease in indentation level.codenode.newlineA placeholder node for line terminators.codenode.indentationA placeholder node for indentation whitespace at the start of a line.codenode.linesdeflines(*items)->'tuple[tuple, ...]':...Convenience function that returns a tuple of lines,
where each argument is the content of one line.Parametersitems:contents of linesReturnstuple of linescodenode.empty_linesdefempty_lines(count:int)->'tuple[Newline, ...]':...Convenience function that returns a tuple of newline nodes.Parameterscount:Number of newlines.ReturnsTuple of newlines.codenode.indenteddefindented(*nodes)->tuple:...Convenience function that returns a tuple containing an indent node,
some inner nodes, and a dedent node.Parametersnodes:inner nodesReturnstuple containing an indent node, inner nodes, and a dedent node.codenode.default_writer_typeDefault Writer type used in codenode.dump and codenode.dumps.codenode.writer.WriterclassWriter:...Processes node trees into strings then writes out the result.Each instance is intended to be used once then discarded.
After a single call to either dump or dumps, the Writer
instance is no longer useful.Methods__init__classWriter:def__init__(self,node:'NodeType',*,indentation='    ',newline='\n',depth=0):...Parametersnode:Base node of node tree.indentation:Initial string used for indents in the output.newline:Initial string used for newlines in the output.depth:Base depth (i.e. number of indents) to start at.process_nodeclassWriter:defprocess_node(self,node)->'Iterable[str]':...Yield strings representing a node and/or apply any of its
associated side effects to the writerfor example:yield indentation string when an indentation node is encounteredincrease the current writer depth if an indent is encounteredappend an iterator to the stack when an iterable is encounteredParametersnode:node to be processedReturnsstrings of text chunks representing the nodedump_iterclassWriter:defdump_iter(self)->'Iterable[str]':...Process and write out a node tree as an iterable of
string chunks.ReturnsIterable of string chunks.dumpclassWriter:defdump(self,stream):...Process and write out a node tree to a stream.Parametersstream:An object with a 'write' method.dumpsclassWriter:defdumps(self):...Process and write out a node tree as a string.ReturnsString representation of node tree.Attributesnode:Base node of node treestack:WriterStack used to iterate over the node treeindentation:Current string used for indents in the outputnewline:Current string used for line termination in the outputdepth:Current output depth (i.e. number of indents)codenode.writer.WriterStackclassWriterStack:...A stack of iterators.
Used by the Writer class to traverse node trees.Each instance is intended to be used once then discarded.MethodspushclassWriterStack:defpush(self,node:'NodeType'):...Converts a node to an iterator then places it at
the top of the stack.Parametersnode:iterable node__iter__classWriterStack:def__iter__(self)->'Iterable[NodeType]':...Continually iterates the top iterator in the stack's items,
yielding each result then popping each iterator off when they
are exhausted.Attributesitems:collections.deque -
Current items in the stack.codenode.nodes.newline.NewlineclassNewline:...Nodes that represent the end of a line.codenode.nodes.depth_change.DepthChangeclassDepthChange:...Nodes that represent a change in indentation depth.Methodsnew_depth_forclassDepthChange:defnew_depth_for(self,depth:int)->int:...Method used to calculate the new depth based on the current one.Parametersdepth:Current depth.ReturnsNew depth.codenode.nodes.depth_change.RelativeDepthChangeclassRelativeDepthChange:...Nodes that represent a change in indentation depth relative to the
current depth by some preset amount.Methods__init__classRelativeDepthChange:def__init__(self,offset:int):...Parametersoffset:Amount by which to increase/decrease depth.Attributesoffset:Amount by which to increase/decrease depth when this node is
processed.codenode.nodes.depth_change.AbsoluteDepthChangeclassAbsoluteDepthChange:...Nodes that represent a change in indentation depth without taking
the current depth into account.Methods__init__classAbsoluteDepthChange:def__init__(self,value:int):...Parametersvalue:Value to set depth to.Attributesvalue:Value to which depth will be set to when this node is
processed.codenode.nodes.indentation.IndentationclassIndentation:...Nodes that represent indentation whitespace at the start of a line.Methodsindents_forclassIndentation:defindents_for(self,depth:int)->int:...Parametersdepth:Current depth.ReturnsNumber of indents to include in whitespace when this
node is processed.codenode.nodes.indentation.RelativeIndentationclassRelativeIndentation:...Nodes that represent indentation whitespace at the start of a line,
with a number of indents relative to the current depth by some
preset amount.Methods__init__classRelativeIndentation:def__init__(self,offset:int):...Parametersoffset:Amount of indents relative to the current depth.Attributesoffset:Amount of indents relative to the current depth that will be
output when this node is processed.codenode.nodes.indentation.AbsoluteIndentationclassAbsoluteIndentation:...Nodes that represent indentation whitespace at the start of a line,
with a number of indents independent of the current depth.Methods__init__classAbsoluteIndentation:def__init__(self,value:int):...Parametersvalue:Amount of indents.Attributesvalue:Amount of indents that will be output when this node is processed.codenode.nodes.indentation.CurrentIndentationclassCurrentIndentation:...Nodes that represent indentation whitespace at the start of a line,
with a number of indents equal to the current depth.codenode.debug.debug_patchdefdebug_patch(writer_type:typing.Type[Writer])->typing.Type[Writer]:...Creates a modified version of a writer type
which prints out some extra info when encountering
an error to give a better ballpark idea of what caused it.
Used in codenode.dump/dumps to implement the debug parameter.Parameterswriter_type:Base writer type.ReturnsNew child writer type with debug modifications.