(Level 4) Share your code as a locally installable Python package
Overview
In this tutorial, you will learn how to share code across any files in your computer. By the end of this tutorial, you will also have your package hosted on GitHub, where you will utilize GitHub Actions to run automatic linting and testing for the incoming code in your GitHub repository.
This tutorial should take about 10 to 15 minutes.
Table of contents
Prerequisites
For Level 4, we assume you have prior experience in developing scientific code in Python.
See also
Are you new to Git and GitHub?
Please read the GitHub workflow section in GitHub workflow to familiarize yourself with the common jargon and terms used. There are also many online resources for learning Git and GitHub.
Install scikit-package with conda
Ensure that
condais installed on your local computer as instructed in (Required) Use conda environment to install packages and run Python code.Ensure that you have added the
conda-forgechannel to yourcondaconfiguration.conda config --add channels conda-forge
Create a new conda environment
skpkg-envand installscikit-packageandpre-commitsimultaneously by running the following command:conda create -n skpkg-env scikit-package pre-commit
Note
If the above command does not work, ensure you have added the
conda-forgechannel to yourcondaconfiguration in Step 2.Activate the environment:
conda activate skpkg-env
Confirm that you have the latest version of
scikit-packageavailable on GitHub:pip show scikit-package
Step 1. Create a new package with scikit-package
Create a new GitHub repository
In this tutorial, we will start by creating a new GitHub repository. The GitHub repository is where we will host our code online (remote).
Visit https://github.com/new.
Enter the
Repository name. You may usemy-packageor any other name you like.Choose
Public.We want to create an empty repository so set
Noneunder Add .gitignore.Set
NoneUnder Choose a license.Click the Create repository green button to create the repository.
Done!
Let’s now create a new package in your computer (local) using scikit-package.
Create a new project with scikit-package
Run the following command to create a new project with
scikit-package:package create system
Respond to the following prompts:
Prompt
Description and example
project_name
The project name. Will be used for the top level folder name. e.g., my-package
github_username_or_orgname
Your GitHub username, or GitHub organization name, where it will be hosted on GitHub. e.g., sbillinge or billingegroup
github_repo_name
The GitHub repository name. Usually the same as the project_name. Use
name-with-hypens. e.g., my-packageconda_pypi_package_dist_name
The name displayed on PyPI and conda-forge. Must not already exist there. Use
name-with-hypens. e.g., my-packagepackage_dir_name
The name of the package directory under
src. Usually the same as the project name but usename_with_underscores. e.g., my_packagecontributors
The contributors involved in the project. e.g., Sangjoon Lee, Simon Billinge
Note
You may press the “Enter” key on your keyboard to accept the default value for the field provided in parentheses.
cdinto the project directory created by thepackage create systemcommand above. We will assume that the user has entered the project name asmy-package.cd my-package
Confirm that you have the following folder structure shown below:
my-package/ ├── LICENSE.rst ├── README.md ├── pyproject.toml ├── requirements │ ├── conda.txt │ ├── pip.txt │ └── tests.txt ├── src │ └── my_package │ ├── __init__.py │ └── functions.py └── tests └── test_functions.pyDone! Let’s now install your package in your local computer where the code can be used in any Python script or Jupyter notebook.
Install your package locally
Create a new conda environment. Let’s call this environment
my-package-env:conda create -n my-package-env python=3.13 \ --file requirements/conda.txt \ --file requirements/tests.txt
Activate the conda environment:
conda activate my-package-env
Build and install the package locally:
pip install -e . --no-deps
Note
What is the
-eflag?The
-eflag indicates that you want to install the package in “editable” mode, which means that any changes you make to the source code will be reflected immediately without needing to reinstall the package. This is useful for development purposes.Note
What is the
--no-depsflag?The
--no-depsflag tells pip not to install any dependencies listed inrequirements/pip.txt. This is because we have already installed the dependencies in the conda environment using the command above.See also
Why is it required to list dependencies both under
pip.txtandconda.txt? Please refer to the FAQ section Dependency management.Then, run the tests using the following command:
pytest
Ensure tests all pass with green checkmarks. Notice that in
tests/test_functions.py, we are importing the locally installed package.Done!
Upload README.md to your GitHub repository
At the moment, the GitHub repository is empty. Let’s create a local branch called main and upload this local branch to the remote GitHub repository.
Follow the series of steps to initialite
Git. You only have to do this once.git init git remote add origin <your-github-repo-url> git branch -M main
Commit the README file to the Git database and push it to the remote GitHub repository:
git add README.md # If you are using Level 4 git add README.rst # If you are using Level 5 git commit -m "docs: add README" git push -u origin main
Note
What’s
origin?originis the default name for the remote repository under your GitHub account. You can think of it as a nickname for the remote repository. You can also use any other name you like, butoriginis the most common convention. For more, please read Do you have a general summary of each term used in the GitHub workflow?.Note
What is
-unext togit push?The
-uflag tells Git to set the upstream (remote) branch for the local branch. This means that in the future, you can simply usegit pushwithout specifying the remote and branch name, and Git will know where to push your changes.
Step 2. Automate code linting and testing with GitHub Actions
Setup pre-commit to lint code before making a commit
First, let’s ensure that before we upload any of our code to the remote repository, we lint the code and ensure that it is formatted correctly. We will use a library called pre-commit to do this.
Configure
pre-committo run each time a new commit is made. You only have to do this once. We assume you have pre-commit installed in your environment by runningconda install pre-commit:pre-commit installLet’s now stage and commit the code:
git add . git commit -m "skpkg: initial commit of a new project"
Ensure that all of the
pre-commithooks pass:black....................................................................Passed prettier.................................................................Passed docformatter.............................................................Passed
Note
blackis a tool that automatically formats Python code to conform to the PEP 8 style guide.prettieris a tool that formats code in various languages, including.md,.rst, and.jsonfiles.docformatteris a tool that formats docstrings in Python code.You will see the new commit in the git log:
git logNote
Did you see any failed
pre-commithooks? If so, no commit will be made. Simply re-rungit add <file>on the files that have been modified bypre-commitand re-enter the same commit message again, such asgit commit -m "skpkg: start a new project with skpkg template". If you are having trouble getting a commit to be accepted, please refer to the FAQ section here.
Setup pre-commit CI in the remote repository in each pull request
Now, we want to ensure pre-commit hooks are also executed when a pull request is made in the remote repository.
Visit https://github.com/apps/pre-commit-ci and click Configure.
Select the repository(s).
Done!
Step 3. Upload rest of files to GitHub repository with pull request
While we previously uploaded the README file to the remote GitHub main repository, this is not a recommended workflow. We want to ensure that before any code is pushed to the main branch, the incoming code formatted, tested, and reviewed. We will try to automate these tasks as much as we can while creating a pull request (PR) to the main branch.
Just in case, pull the latest code from the remote
mainbranch to your localmainbranch:git checkout main git pull origin main
Checkout a new branch called
skpkg-systemfrom themainbranch:git checkout -b skpkg-system git add . git commit -m "skpkg: start a new project with skpkg" git push -u origin skpkg-system
Visit your GitHub repository online.
Click on the new green button that says
Compare & pull request.The PR title can be
skpkg: start a new project with skpkg template.The
basebranch should bemainand thecomparebranch should beskpkg-system.Click on the
Create pull requestbutton.Wait for
Tests on PRto run and pass. It runspyteston the incoming code in each pull request.Also wait for
pre-commitCI to run and pass.Note
Did
pre-commit CIfail?If the pre-commit failed, you will need to first pull the new commit created by
pre-commit CIbefore making any new edits locally. You can do this by running the following command:git pull origin skpkg-system git add <file-modified-that-fixes-pre-commit-error> git commit -m "chore: <your commit message>" git push
Click Files changed in the PR to to review the new files added to the repository.
Once reviewed, click Merge pull request.
Delete the
skpkg-systemremote branch after merging.Visit your GitHub repository and confirm that the
mainbranch is updated.Congratulations! You are done!
(Recommended) How to develop your code moving forward using pull requests
Assume that you have successfully followed the previous steps. Now, you want to add new code to your GitHub repository. Perhaps you are working with a group of people. Here is a high-level overview with step-by-step instructions on how to do that:
Pull the latest code from the remote
mainbranch:git checkout main git pull origin main
Note
Recall that we used the name
originas the nickname for the remote GitHub repository.Ensure that your local
mainbranch is synced with the remotemainbranch by running:git logCreate a new local branch from the
mainbranch. Let’s call this branchskpkg:git checkout -b <branch-name>
Modify any file that you want. Then, stage and commit the changes:
git add <file-modified-added-deleted> git commit -m "feat: <your commit message>"
Push your code from
<branch-name>to the remote<branch-name>branch:git push --set-upstream origin <branch-name>
Visit your GitHub repository.
Create a PR from
origin/<branch-name>toorigin/main.Wait for the
Tests on PRandpre-commitchecks to pass.Merge the PR, delete the branch.
Repeat the steps in this section.
Done!
What’s next?
Once you are ready to release your package to the wider world, let’s proceed to (Level 5) Share your code as a publicly installable package where you will learn to release your package to PyPI and conda-forge so that your package can be installed by anyone in the world.