<jupyter_start><jupyter_text>Structuring Code for Release Modules and Packages* As code gets more involved, it becomes unwieldy & unnatural to keep everything in the same file, or even the same folder* Functions from other codes made for different reasons might be useful elsewhere* Useful to break up code into modules and packages  - used like ‘package.module’* **Module**: file containing defined functions & variables. **It must have a .py extension.*** **Package**: a properly-organized folder containing modules (packages Numpy are well-developed examples - you can make your own)<jupyter_code>%ls /Users/jbloom/anaconda/envs/seminar/lib/python3.5/site-packages
!pip install twilio
from twilio import jwt
%ls /Users/jbloom/anaconda/envs/seminar/lib/python3.5/site-packages/twilio/jwt/<jupyter_output>__init__.py  [34m__pycache__[m[m/<jupyter_text>Modules: Setting up your path`PYTHONPATH`Augment the default search path for module files. The format is the same as the shell’s PATH: one or more directory pathnames separated by os.pathsep (e.g. colons on Unix or semicolons on Windows). Non-existent directories are silently ignored.In addition to normal directories, individual PYTHONPATH entries may refer to zipfiles containing pure Python modules (in either source or compiled form). Extension modules cannot be imported from zipfiles.The default search path is installation dependent, but generally begins with prefix/lib/pythonversion (see PYTHONHOME above). It is always appended to PYTHONPATH.An additional directory will be inserted in the search path in front of PYTHONPATH as described above under Interface options. The search path can be manipulated from within a Python program as the variable sys.path.Add to your .bashrc, .cshrc, or .tcshrc file:```bashBASH Style: export PYTHONPATH=/path/to/your/code:$PYTHONPATHCSH Style: setenv PYTHONPATH /path/to/your/code```<jupyter_code>import sys
# Get a list of all paths python is looking at with sys.path
print(sys.path[-4:])  # only look at the first 4 to save space
# Can append to this list:
# sys.path.append(“/new/software/path/”)<jupyter_output>['/Users/jbloom/anaconda/envs/seminar/lib/python3.5/site-packages/datashader-0.4.0-py3.5.egg', '/Users/jbloom/anaconda/envs/seminar/lib/python3.5/site-packages/yweather-0.1.1-py3.5.egg', '/Users/jbloom/anaconda/envs/seminar/lib/python3.5/site-packages/IPython/extensions', '/Users/jbloom/.ipython']<jupyter_text>New paths appended will not be preserved upon exiting python. For long-term path appending, use PYTHONPATH environment variable defined in previous slide.  Packages"Packages are a way of structuring Python’s module namespace by using “dotted module names”. For example, the module name A.B designates a submodule named B in a package named A. Just like the use of modules saves the authors of different modules from having to worry about each other’s global variable names, the use of dotted module names saves the authors of multi-module packages like NumPy or the Python Imaging Library from having to worry about each other’s module names." [Python tutorial](https://docs.python.org/3/tutorial/modules.html)* If path is set correctly, code can be broken up into reasonable folders and imported as necessary, either by importing entire modules (.py files) or functions/classes within the modules.* Put an `__init__.py` file in each folder you want to be able to import from.* Code in `__init__.py` is run when the package, or any derivative of it, is imported.  Often `__init__`.py is an empty file.* if a package’s `__init__.py` code defines a list named `__all__`, it is taken to be the list of module names that should be imported when from package import * is encountered Example:```presetup.pyREADME.mdLICENSE.txtsound/                      Top-level package  __init__.py               Initialize the sound package  formats/                  Subpackage for file format conversions      __init__.py      wavread.py      wavwrite.py      aiffread.py      aiffwrite.py      auread.py      auwrite.py      ...  effects/                  Subpackage for sound effects      __init__.py      echo.py      surround.py      reverse.py      ...  filters/                  Subpackage for filters      __init__.py      equalizer.py      vocoder.py      karaoke.py      ...```  Distributions - distutils2 * distutils2: the standard way to take your directories of code and bundle them up for easy installation and use by others* You create a setup.py file which allows others to install your code in the standard fashion.* There are myriad options for the metadata you can define, an incredibly simple example is below:```pythonfrom distutils2.core import setupsetup(name='My_Package',      version='0.01',       license='License_to_not_kill',      py_modules=['my_package_name'],      )```http://pythonhosted.org/Distutils2/distutils/introduction.htmlhttp://pypi.python.org/pypi/Distutils2Running the `setup.py` will install your package (and modules) into the Python pathIf you’ve written your own package, use distutils2 to create a standard, share-able zipped file```bashcd My_Package_folderpython setup.py sdist[... created my_package_name-0.1.tar.gz ...]```http://guide.python-distribute.org/  Putting it all togetherSee package management: https://packaging.python.org/```bash tree PyAdder/ -a -T PyAdder -C --noreport -I ".git|*.pyc"PyAdder/├── .gitignore            files/paths to ignore if you are git├── .travis│   └── run.sh            executable telling travis what to run (ie. tests)├── .travis.yml           directive to Travis├── CHANGES.txt├── LICENSE.txt           Be nice. Set a license.├── MANIFEST.in├── README.md             should always have a readme├── adder                 name of your package│   ├── __init__.py│   └── tests                     put your test in separate directories │       ├── __init__.py│       └── test_one_number.py├── requirements.txt              python requirements├── setup.cfg└── setup.py```Let's look at the repo: https://github.com/profjsb/PyAdder<jupyter_code>cd PyAdder/
!python setup.py test
!python setup.py sdist bdist_wheel
ls dist/
!pip install dist/PyAdder-0.0.1-py3-none-any.whl
import adder
adder.run(1,2,10,-1)<jupyter_output><empty_output><jupyter_text>PyPiLet's upload to PyPi (see http://peterdowns.com/posts/first-time-with-pypi.html)<jupyter_code># python setup.py sdist upload -r pypi
https://pypi.python.org/pypi?name=PyAdder&version=0.0.1&:action=display<jupyter_output><empty_output><jupyter_text>Now you should be able to pip install this:<jupyter_code>!pip install pyadder<jupyter_output>Requirement already satisfied: pyadder in /Users/jbloom/anaconda/envs/seminar/lib/python3.5/site-packages
{'fraction_non_alphanumeric': 0.0912621359223301, 'fraction_numerical': 0.0074682598954443615, 'mean_word_length': 3.94169741697417, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 18, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '323574', 'license': 'MIT', 'n_tokens_mistral': 1999, 'n_tokens_neox': 1930, 'n_words': 717, 'path': '01_Versioning_Application_Building/02_Structuring Code for Release.ipynb', 'repo_name': 'thomascarey/python-ay250-lectures', 'size': '6695'}