pyMez_Module_Specification

# pyMez Module Specification

##
1. Module Naming Convention

Modules are named as UpperCamelCase (breaking from python tradition)

> GeneralModels

##
2. Module Info and Docstring

Module Info is a comment block with stuff like your name and license. The Module Docstring is the first quoted string and should provide information about the module. It is my habit to keep docstrings to a paragraph with links for expanded examples and help documentation. For example:

    """
    Graph Models stores sub classes of graphs that define data translations. All edges
    or the functions that define translations from one format to another
    are found in <a href="./Translations.m.html">`pyMez.Code.DataHandlers.Translations`</a>.
    Currently, the module networkx is used to display the graph.

    Examples
    --------
        #!python
        >>from pyMez import *
        >>image_graph=ImageGraph()
        >>image_graph.set_state('png','my_png.png')
        >>image_graph.move_to_node('EmbeddedHtml')
        >>output=image_graph.data
        >>print output

    <a href="../../../Examples/Html/GraphModels_Example.html">GraphModels Example</a>

    Requirements
    ------------
    + [sys](https://docs.python.org/2/library/sys.html)
    + [os](https://docs.python.org/2/library/os.html?highlight=os#module-os)
    + [networkx](http://networkx.github.io/)
    + [numpy](http://www.numpy.org/)
    + [pyMez](https://github.com/aricsanders/pyMez)

    Help
    ---------------
    <a href="./index.html">`pyMez.Code.DataHandlers`</a>
    <div>
    <a href="../../../pyMez_Documentation.html">Documentation Home</a> |
    <a href="../../index.html">API Documentation Home</a> |
    <a href="../../Examples/Html/Examples_Home.html">Examples Home</a> |
    <a href="../../../Reference_Index.html">Index</a>
    </div>
       """

##
3. Module Imports

1. Standard Imports
Imports that are in the standard library and should always work if you have python installed properly. They should be namspaced,

    import os

2. 3rd Party Imports
Imports from within the user defined package or other places that HAD to be loaded separately. These imports should have a try: except: structure. That is, if they fail the program should default to something useful.

    try:
            from Code.DataHandlers.GeneralModels import *
        except:
            print("The module pyMez.Code.DataHandlers.GeneralModels was not found,"
                  "please put it on the python path")
            raise  ImportError

##
3. Module Constants

The constants that can be used through out the module. Constant names should be ALL_UPPERCASE_WITH_UNDERSCORES.

    S2P_COMPLEX_COLUMN_NAMES=["Frequency","S11","S21","S12","S22"]

##
4. Module Functions

A module function takes a variable(s) and maps it to another variable(s). Function names should be all_lowercase_with_underscores.

    def x_squared(x):
      "A function that maps x->x**2"
      return x**2

##
5. Module Class Definitions

A module class is a complex data container which can inherit functions (methods) and variables (attributes) from other objects. They are the foundation of Object Oriented programming. Module names should be TitleCaseWithNoSpaces. Methods and attributes should be either all_lowercase_with_underscores or lowerThenTitleCaseWithNoSpaces. It is common for me to define aliases for methods so that both MyClass.myMethod() and MyClass.my_method() work.

    class MyClass():
      "An empty class for demonstration"
      pass

##
6. Module Scripts

A module script is a function or procedure that does something and returns at most a boolean value. The reason they are not defined with module functions is so that they can use all of the class definitions. The reason they are not only in the Module runner is so that they can be accessed by importing the module. I usually try to put one of the following words in the name: test,script or robot and the name is all_lowercase_with_underscores.

    def test_f(x_test=2):
        "Tests the function f"
        print("The Result of f({0}) is {1}".format(x_test,f(x_test)))

##
7. Module Runner

The statement `if __name__ == '__main__':` at the end of the module determines the module's behavior if it is opened as a file or ran from a shell prompt as `$python ModuleName.py`.

I usually just call module script(s) of interest here.

    if __name__=='__main__':
      test_f()

</ol> Notes:

There are several exceptions to the naming rules

1. The word py in my code is always lowercase
2. The words HTML and XML are always capitalized
3. If there is a case sensitive quantity in the name of a function it has it's original case get_Id()

4. Avoid relative imports such as from . import a, instead inject a folder above it into sys.path and import the module using the full name space, this keeps the module from breaking when called as a script.

       sys.path.append(os.path.join(os.path.dirname( __file__ ), '..','..'))
       try:
        from Code.Utils.Alias import *
        METHOD_ALIASES=1
       except:
        print("The module pyMez.Code.Utils.Alias was not found")
        METHOD_ALIASES=0
        pass

## An example¶

    #-----------------------------------------------------------------------------
    # Name:        alias.py
    # Purpose:     Function that provides exec string to define common aliases for
    #              for functions
    # Author:      Aric Sanders
    # Created:     2/21/2016
    # License:     MIT License
    #-----------------------------------------------------------------------------
    """ Module that defines functions for handling alias definitions in Classes """

    #-------------------------------------------------------------------------------
    # Standard imports

    import re
    import types

    #-------------------------------------------------------------------------------
    # Module Functions

    def alias(object):
        """ Creates aliases that map all non built-in methods to
        both lowerCapitalCase and all_lower_with_underscore naming conventions the
        output is a list of strings to be used with exec(list[i]) """

        old_names=[]
        split_name=[]
        exec_list=[]
        new_name=''

        # Get all the atributes without __ in the begining
        for attribute in dir(object):
            if not re.match('_',attribute):
                try:
                    if type(eval('object.%s'%attribute)) is types.MethodType:
                        old_names.append(attribute)
                except:pass
        # If they are camelCase make them all lower with underscores or vice versa
        for name in old_names:
            if re.search(r'[A-Z]+',name) and not re.search(r'_',name):
                split_upper_case=re.split(r'[A-Z]+',name)
                upper_matches=re.findall(r'[A-Z]+',name)
                for index,piece in enumerate(split_upper_case):
                    if index<len(upper_matches):
                        new_name=new_name+piece+'_'+upper_matches[index].lower()
                    else:
                        new_name=new_name+piece
                exec_list.append(('self.'+new_name+'='+'self.'+name))

            elif re.search(r'_',name):
                split_name=name.split('_')
                for index,piece in enumerate(split_name):
                    if index==0:
                        new_name=piece
                    else :
                        new_name=new_name+piece.title()
                exec_list.append(('self.'+new_name+'='+'self.'+name))
            #else: pass
        return exec_list

    #-------------------------------------------------------------------------------
    # Class Definitions

    class MyTestClass():
        """ A class to test the alias function: call it in a for loop inside of
        __init__() method """

        def __init__(self):
            self.littleAttribue=[]
            self.i_like_underscores=[]
            # this calls and executes the alias function
            for command in alias(self):
                exec(command)

        def myFunctionNumberOne(self):
            pass
        def my_function_number_two(self):
            pass
        def my_funtion_Number_three(self):
            pass

    #-------------------------------------------------------------------------------
    # Module Scripts
    def test_alias():
        print " Before making the instance the attributes defined are:"
        for attribute in dir(MyTestClass):
            print 'Attribute Name: %s'%(attribute)
        new=MyTestClass()

        print " After making the instance the attributes defined are:"
        for attribute in dir(new):
            print 'Attribute Name: %s'%(attribute)
    #-------------------------------------------------------------------------------
    # Module Runner

    if __name__ == '__main__':

        test_alias()