Skip to content

Commit

Permalink
Fix: minor edits to first tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
lwasser committed Nov 13, 2023
1 parent fc1f0c3 commit d02f590
Showing 1 changed file with 92 additions and 54 deletions.
146 changes: 92 additions & 54 deletions tutorials/2-installable-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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!)
Expand All @@ -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").

Expand All @@ -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/
Expand All @@ -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
Expand Down Expand Up @@ -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. <add link>
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.

<!-- # TODO: add link to repo when it's ready
<!--
# TODO: add link to repo when it's ready
# below: add links to the packaging guide for build tools etc etc... -->

### 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:

Expand Down Expand Up @@ -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"

Expand All @@ -244,21 +264,24 @@ 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:

1. A project directory structure with a `pyproject.toml` file at the root
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
<!--
NOTE: just in case we may want to include the bash lessons here?? -->

- 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
Expand All @@ -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.
Expand All @@ -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...
<!-- > 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.
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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.
Expand Down

0 comments on commit d02f590

Please sign in to comment.