diff --git a/tutorials/2-installable-code.md b/tutorials/2-installable-code.md index 499f1d78..b9a14fca 100644 --- a/tutorials/2-installable-code.md +++ b/tutorials/2-installable-code.md @@ -16,16 +16,27 @@ In this lesson you will learn: - How to create a basic `pyproject.toml` file to declare dependencies and metadata - How to declare a build backend which will be used to build and install your package (learn more about what build back ends are here - link to guide) - How to install your package in editable mode for interactive development - ::: + +To complete this lesson you will need a local Python (development) +environment. You are welcome to use any environment manager that you chose. [Review this lesson,](1-create-environment) which walks you through creating an environment using both `venv` and `conda`. If you aren't sure which environment manager to use and +you are a scientist, we suggest that you use `conda`. +::: ## Make your package installable -It’s time to create your Python package. To make your code installable you need: +It’s time to create the most basic version of a Python package. +While this code can't be yet published to PyPI or conda and +is not documented, it will be installable on your computer or +anyone elses. + +To make your code installable you need: - A `pyproject.toml` file - An (optional but recommended) `__init__.py` file in your code directory -- a specific directory structure -- some code. +- A specific directory structure +- Some code. + +### What does a basic package directory structure look like? The directory structure you’ll create in this first section looks like this: @@ -35,29 +46,31 @@ pyospackage/ └─ pyproject.toml └─ src/ # the src directory ensures your tests always run on the installed └── pyospackage/ # package directory where code lives, use the package name - ├── __init__.py + ├── __init__.py ├── add_numbers.py └──# Add any other .py modules that you want here ``` If you already know what all of the elements of this package structure are, you can skip to the next lesson. Otherwise keep reading to learn about each element of the above structure. -**Maybe have steps here and then add the explanation below??** +### Above the above package directory structure -Notice a few things about the above layout +Notice a few things about the above layout: -1. Your package code lives within a `src/packagename` directory. While it’s fine if you wish to use a [flat layout](https://www.pyopensci.org/python-package-guide/package-structure-code/python-package-structure.html#about-the-flat-python-package-layout) containing no `src/` directory, we suggest a src directory as it will make it easier for you to ensure you are always running tests on the installed version of your code. [More here on that](https://www.pyopensci.org/python-package-guide/package-structure-code/python-package-structure.html#the-src-layout-and-testing) -2. Within the `src/` directory you have a package directory called pyospackage/. Use the name of your package for that directory name.. - 1. You also have an `__init__.py` file and all of your python modules in your package directory. -3. The pyproject.toml file lives at the root directory of your package. +1. Your package code lives within a `src/packagename` directory. While it’s fine if you wish to use a [flat layout](https://www.pyopensci.org/python-package-guide/package-structure-code/python-package-structure.html#about-the-flat-python-package-layout) (a flat layout does not have a src/ directory at the root), we suggest that you use `src/` directory as it ensure you are running tests on the installed version of your code. [Learn more here.](https://www.pyopensci.org/python-package-guide/package-structure-code/python-package-structure.html#the-src-layout-and-testing) +2. Within the `src/` directory you have a package directory called `pyospackage/`. Use the name of your package for that directory name. +3. In your package directory, you have an `__init__.py` file and all of your Python modules. +4. The `pyproject.toml` file lives at the root directory of your package. ## Init.py and pyproject.toml files -The `__init__.py` and pyproject.toml files in the above layout are important to understand. +The `__init__.py` and `pyproject.toml` files in the above layout +are important to understand. More on that below. ### What is an init.py file? -The `__init__.py` file tells python that the directory it’s in should be treated as a Python package. The `__init__.py` file also +The `__init__.py` file tells Python that the directory it’s in should be treated as a Python package. +The `__init__.py` file also: - Allows you to organize multiple modules within the package. - Allows you to create shortcuts for importing specific functions, and classes into your code (more on that later!) @@ -73,35 +86,37 @@ You can technically install a package without an `__init__.py` file since Python The **pyproject.toml** file is: -- where you store your project’s metadata (including its name, authors, license, etc) -- where you store dependencies (the packages that it depends on) -- used to specify and configure what build back end you want to use to create your package. +- Where you store your project’s metadata (including its name, authors, license, etc) +- Where you store dependencies (the packages that it depends on) +- Used to specify and configure what build back end you want to use to create your package. A **pyproject.toml**: This file is critical for both installing your package and publishing to pyPi. This is where you will declare your project build system tools, dependencies and metadata. More on that later -After the `__init__.py` and pyproject.toml files have been added, your package can be built and distributed as an installable Python package using tools like pip. +After the `__init__.py` and `pyproject.toml` files have been added, your package can be built and distributed as an installable Python package using tools like pip. Note that the `pyproject.toml` file needs to have the a few basic items defined for this to work including: -Note that the `pyproject.toml` file needs to have the a few basic things for this to work including +- The build backend that you want to use, +- The project name, and a few other metadata elements. -- the build backend that you want to use, -- the project name, and a few other metadata elements. +:::{admonition} pro tip +:class: tip -Tip: **If you try to pip install a package with no `pyproject.toml` you will get the following error:** +The `pyproject.toml` file replaces some of the functionality of both the setup.py file and setup.cfg files. +If you try to pip install a package with no `pyproject.toml` you will get the following error: ```bash GitHub/pyospackage/testme ➜ pip install . -ERROR: Directory '.' is not installable. Neither 'setup.py' nor 'pyproject.toml' found. +ERROR: Directory '.' is not installable. +Neither 'setup.py' nor 'pyproject.toml' found. ``` -**Tip:** The `pyproject.toml` file replaces some of the functionality of both the setup.py file and setup.cfg files. -{: .notice .notice--tip} +::: -## Try it yourself - Create your package structure! +## Try it yourself - Create your package! -Let’s get started. Create a directory structure similar to the structure below. If you don’t wish to make each of the elements below, you can always [fork and clone and customize the pyOpenSci example package, here](https://github.com/pyOpenSci/pyosPackage) +Now that you understand the basics, it's time to create a Python package! Create a directory structure similar to the structure below. If you don’t wish to create each of the files and directories below, you can always [fork and clone and customize the pyOpenSci example package, here](https://github.com/pyOpenSci/pyosPackage) -## Step 1: Set Up the Package Directory Structure +### Step 1: Set Up the Package Directory Structure Create a new directory for your package. Choose a name for your package, preferably in lowercase and without spaces (e.g., "pyospackage"). @@ -126,15 +141,19 @@ pyospackage/ ├── __init__.py ``` -## Step 2: Add Code to Your Package +### Step 2: Add code to your package Within the `pyospackage` subdirectory, add 1 or more Python modules (.py files) containing the code that you want your package to access and run. -If you don't have code already and are just learning how to create a python +If you don't have code already and are just learning how to +create a Python package, then create an empty `add_numbers.py` file. -**Tip:** When you see the word module, we are referring to a .py file containing Python code. -{: .notice .notice--info } +:::{admonition} pro tip +:class: tip + +When you see the word module, we are referring to a `.py` file containing Python code. +::: ``` pyospackage/ @@ -146,15 +165,15 @@ pyospackage/ ``` -## Add some code to your add_numbers module +### Step 3. Add code to your `add_numbers` module -If you are following along and making a python package from scratch then you can add the code below to your `add_numbers.py` module. The function below adds two integers together and returns the result. Notice that the code below has a few features that we will review in future tutorials: +If you are following along and making a Python package from scratch then you can add the code below to your `add_numbers.py` module. The function below adds two integers together and returns the result. Notice that the code below has a few features that we will review in future tutorials: 1. It has a [numpy-style docstring ](https://www.pyopensci.org/python-package-guide/documentation/write-user-documentation/document-your-code-api-docstrings.html#three-python-docstring-formats-and-why-we-like-numpy-style) 2. It uses [typing](https://www.pyopensci.org/python-package-guide/documentation/write-user-documentation/document-your-code-api-docstrings.html#adding-type-hints-to-your-docstrings) If you aren’t familiar with docstrings or typing yet, that is ok. We will get -to it later in our tutorial series. Or You can review the pyOpenSci [packaging guide](https://www.pyopensci.org/python-package-guide/documentation/write-user-documentation/document-your-code-api-docstrings.html) +to it later in our tutorial series. Or, you can review the pyOpenSci [packaging guide](https://www.pyopensci.org/python-package-guide/documentation/write-user-documentation/document-your-code-api-docstrings.html) for an overview. ```python @@ -184,16 +203,17 @@ def add_num(a: int, b: int) -> int: return a + b ``` -## Add metadata to your `pyproject.toml` file +### Step 4. Add metadata to your `pyproject.toml` file -Next, you will add some information to your `pyproject.toml` file. You are -are welcome to copy the file we have in our example repo here. +Next, you will add some metadata (information) to your `pyproject.toml` file. You are +are welcome to copy the file we have in our example repo here. - -### A brief overview of the TOML file +#### A brief overview of the TOML file The TOML format consists of tables and variables. Tables are sections of information denoted by square brackets: @@ -227,7 +247,7 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "pyospackage_gh_user_name" # rename this if you plan to publish to test pypi +name = "pyospackage_gh_user_name" # rename this if you plan to publish to test PyPI # Here you add the package version manually. You will learn how to setup # dynamic versioning in a followup tutorial. version="1.1" @@ -244,7 +264,7 @@ The core basic information that you need in a `pyproject.toml` file in order to rarely update it. In the next lesson you’ll add more metadata and structure to this file. ::: -## Install your package locally +### Step 5. Install your package locally At this point you should have: @@ -252,13 +272,16 @@ At this point you should have: 2. A package directory containing an empty `__init__.py` file and 3. At least one Python module (e.g. `add_numbers.py`) -You are now ready to install (and build) your Python package!. +You are now ready to install (and build) your Python package! Let’s try it out. -- First `cd` into your package directory + + +- First open bash and `cd` into your package directory - Activate the Python environment that you wish to use. If you need help with working with virtual environments [check out this lesson](1-create-environment). -- Next, open bash and +- Finally run `python -m pip install -e .` ```bash # Activate your environment using conda or venv @@ -281,21 +304,29 @@ Obtaining file:///Users/leahawasser/Documents/GitHub/pyos/pyosPackage # use pip list instead of conda list here if you are working in an venv environment rather than a conda envt ``` -`python -m pip install -e .` installs your package into the current active -Python active environment in **editable mode**. Installing your package in +Before we go any further, let's break down this command: + +`python -m pip install -e .` (e for editable) + +`pip install -e .` installs your package into the current active +Python environment in **editable mode**. Installing your package in editable mode, allows you to work on your code and then test the updates -interactively. If you wish to install the package regularly (not in editable +interactively in your favorite Python interface. One important caveat of editable mode is that every time you update your code, you may need to restart your Python kernel. + +If you wish to install the package regularly (not in editable mode) you can use: - `python -m pip install . ` :::{admonition} :class: tip -`python -m` you use this to ensure that you are calling the version of pip associated with your current active environment. +Above, you use`python -m` to call the version of pip installed into your current active environment. ::: +### 6. Test out your new package + After installing your package, type “python” at the command prompt to start -a Python session in your active python environment. +a Python session in your active Python environment. You can now import your package and access the `add_num` function. @@ -308,9 +339,9 @@ Type "help", "copyright", "credits" or "license" for more information. 3 ``` -## Customize access to Python functions using the `__init__.py` file +## OPTIONAL: Customize access to Python functions using the `__init__.py` file -> TODO: note that they may or may not understand modules etc... I have some text on this but make sure to link to that in the intro tutorials on the basics of packaging. Could this also introduce problematic behavior if users are learning and just add functions to the init file...it could... + Let's make one more tweak to the code. @@ -329,10 +360,13 @@ from pyospackage.add_numbers import add_num Save the file. -Now, open up a NEW python terminal or restart your Python kernel. +Now, open up a NEW Python terminal or restart your Python kernel. + +:::{admonition} Don't forget to restart your Python kernel! +:class: important It's important that you restart your Python kernel if you wish to access the changes to your code that you just made. -{: .notice .notice--important } +::: ```python > python @@ -348,10 +382,14 @@ The decision to add specific functions, methods or classes to your considering what functionality in your package you want to "elevate" to the top level vs. what makes the most sense to keep in individual modules. +:::{important} + > TODO: Guidelines for when and how to add methods and such to the `__init__.py` file here. > `__all__ = ['add_num']` +::: + ### Congratulations! You created (the beginning of) your first Python package You did it! You have now created a Python package that you can install into any Python environment. While there is still more to do, you have completed the first major step. @@ -360,7 +398,7 @@ In the next lesson you will: 1. Flesh out metadata for your package within your `pyproject.toml` file. 2. Learn how to build your learn how to build your package distribution files (**sdist** and **wheel**) and -3. Learn how to publish your package on **testpypi**. +3. Learn how to publish your package on **test PyPI**. If you have a package that is ready for the mainstream user then you can also publish your package on PyPI.