Python projects are much easier to use and maintain if we standardize around the following:
- Dependency Management.
- Technical Documentation Management (via MkDocs).
- Packaging (via setuptools).
- Exposing CLI Tools (via 'Command Line Scripts').
- Automatic Testing (via pytest).
- Continuous Integration and Deployment (via Jenkins).
This repository is a starting point for any python project! You should clone this project and make updates as indicated below.
Before jumping into details and instructions, it is worth reviewing our project structure in the file system.
project_template
├── docs
│ ├── index.md
│ ├── development.md
│ ├── release-notes.md
│ └── user-guide.md
├── MANIFEST.in
├── mkdocs.yml
├── README.md
├── setup.py
├── src
│ └── examplepkg
│ ├── hello_world.py
│ └── __init__.py
└── tests
└── test_hello_world.py
The project structure can be conceptually split into three chunks:
-
The first several files are part of project documentation:
├── docs │ ├── index.md │ ├── development.md │ ├── release-notes.md │ └── user-guide.md ├── MANIFEST.in ├── mkdocs.yml ├── README.md
-
Then there is the production code:
├── setup.py ├── src │ └── examplepkg │ ├── hello_world.py │ └── __init__.py
-
And finally, the test code:
└── tests └── test_hello_world.py
If you have not used this project structure, I recommend getting the
examplepkg
up and running. Read throughdocs/development.md
to getexamplepkg
installed -- including thehello
command-line tool!
To get started using this project structure for your own project, here are the initial steps:
Further instructions are provided in "TODO: setup your..." sections below.
- Rename the
examplepkg
directory in thesrc/
directory.- For public projects, we try to follow PEP8 convention: "Python packages should have short, all-lowercase names... the use of underscores is discouraged."
- For private projects, underscores should be used in the name if it improves readability (or if it makes naming easier)!
- Delete (or rename)
hello_world.py
andtest_hello_world.py
- Replace this
README.md
withREADME.md.template
.- You can continue to reference this (meta) README.md in our SCM server: https://git.uoregon.edu/projects/ISN/repos/python-project-structure/browse
Dependency management is covered in our Developer docs.
ℹ See Developer docs that discuss Dependency management in depth.
- Ensure that production and development dependencies are sufficient but not too restrictive.
- E.g., double check that 'best practices' are followed, as outlined in the Developer docs.
⚠ When Hatch gains support for lock files, we will update this process to include using a lock file for maximum (CICD) stability.
We use MkDocs to manage project documentation. There are several pieces of documentation to review
├── docs
│ ├── index.md
│ ├── development.md
│ ├── release-notes.md
│ └── user-guide.md
├── MANIFEST.in
├── mkdocs.yml
├── README.md
The
docs/
directory is perfectly readable as Markdown without even installing MkDocs! E.g., It works perfectly well when viewed on your SCM server, in VSCode, as well as other 'Markdown Rendering' tools!
docs/
contains documentation for the entire project.MANIFEST.in
exists to ensure that setuptools packaging process include thedocs/
directory.mkdocs.yml
is a MkDocs configuration file, used to 'serve' thedocs/
directory using MkDocs (discussed indocs/README.md
).README.md
is just a pointer todocs/README.md
(seeREADME.md.template
).
- Provide your project's name in
mkdocs.yml
- Search through the
docs
directory for "TODO"s.
Packaging is all done via Hatch.
ℹ Tip: Search for the word "TODO" in "pyproject.toml" and "hatch.toml"
- Replace any instance of "
example-package
" with "my-package
" - Replace any instance of "
example_package
" with "my_package
"
ℹ Why use both hyphens and underscores? This follows PyPI.org common practices:
- We use hyphens for package's published-name, enabling
pip install my-package
- Yet we MUST use underscores for the package's directory name.
ℹ This convention is against PEP8... So, why do this? This is for practical use of setuptools + pip: underscores are actually incompatible with setuptools when using our pypi.uoregon.edu server. See (somewhat) relevant stack overflow answer
-
If you want to expose any command-line tools from your package, update
project.scripts
. -
Finally, search in "pyproject.toml" and "hatch.toml" for any remaining "TODO"s.
- E.g., description, keywords, classifiers.
The basics of automatic testing with pytest is discussed at the end of our developer guide (docs/development.md
).
⚠ NOTICE: Work is in progress to transition to using GitHub Action Workflows instead of our private Jenkins server.
We use a Jenkins Shared Library to provide a single command to enable CICD for any python project hosted on our git.uoregon.edu server.
There are a couple of pieces that we need to establish before CICD will work for your new python project. These include updating your project's Jenkinsfile, and updating Bitbucket to trigger your Jenkins job. In short, you will have to do the following, discussed in detail below:
- Configure Jenkinsfile (choose a Python Package Index).
- Set up "Parameterized Build for Jenkins" hook in your Bitbucket repo.
Our Jenkins server will automatically create a pipeline for any repo in the ISN project, via a Jenkins Organization Folder.
If using pypi.uoregon.edu, there are some additional one-time setup instructions for your package (internal docs)
You must decide whether you will be publishing to pypi.org or our private pypi.uoregon.edu server.
- Simply update the
packageIndex
in your Jenkinsfile. Choose from:- 'pypi.org' or,
- 'pypi.uoregon.edu'
See all current options for 'buildPythonProject' (and default values) at defaultPythonProjectConfig.groovy
Our default settings and how to enable them is well discussed in our internal documentation.
In short, these are the steps to enable the Bitbucket Webhook in our ISN project:
- Navigate to your "Repository Settings → Hooks"
- Change from 'Inherited (disabled)' to "Enabled" for Parametrized build for Jenkins.
- Update the 'Job Name' to align with the Jenkins Job URL.