PyPI package: Creating the first feature

Author

pydatk

Published

June 18, 2025

Introduction

This is the second in a series of posts about creating a basic Python package and publishing it on PyPI.

In the previous post, I covered GitHub repo setup, environments, a basic release process and package setup.

In this post, I’ll create the package’s first feature.

I’ll be setting up my own project (pydatk) during these posts. You can download a zip file containing the code for the first version of my project from GitHub.

For this post I’ll be using:

  • Python 3.13
  • Ubuntu 24.04.2 LTS
  • VS Code 1.100.3
  • Other tools as mentioned in the text.
Feedback or questions?

If you have any feedback or questions I’d love to hear from you. You can comment on posts, use the website and pydatk forums or email me.

Coding

The first feature will be a simple financial calculator, returning the effective interest rate for a given nominal rate and number of compounding periods.

Here’s an example from Wikipedia: A nominal interest rate of 6% compounded monthly is equivalent to an effective interest rate of 6.17%. 6% compounded monthly is credited as 6%/12 = 0.005 every month. After one year, the initial capital is increased by the factor (1 + 0.005)12 ≈ 1.0617. [1]

I’ll add this function to src/pydatk/finance.py:

def effective_rate(nom, nper):
    return float((1 + (nom / nper)) ** nper - 1)

Testing

I’ll test the new function from the terminal. In my project (yours might be different), these commands have to be run from the src directory (the directory below the package itself):

  • project root
    • src
      • pydatk

I’ll start by testing that I can import the new package:

$ python
Python 3.13.3 (main, Apr  9 2025, 08:55:03) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pydatk
>>> exit()

I’ll then import the new finance module directly and test the new function:

$ python
Python 3.13.3 (main, Apr  9 2025, 08:55:03) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pydatk import finance
>>> finance.effective_rate(0.06, 12)
0.06167781186449828
>>> exit()

In the above test, the input arguments are from the example in the Coding section. The function returns the expected result.

Namespaces and package alias

As the pydatk package grows, I will add additional modules. Importing each module using the from pydatk import modulename syntax is inconvenient if the script I’m writing uses multiple modules.

Module names in a given namespace [2] also need to be considered with the above syntax: The name of the package (pydatk) should be safe in any namespace because it’s an uncommon word and reserved in PyPI, so it’s unlikely to be used by someone else. But the name of the module (finance) is very common and is likely to be used as a module name in a different package.

Note

I’m thinking of writing a post on object naming and reserved words in Python and associated tools (e.g. pandas or Postgres) - please leave a comment below if you’re interested.

These are just two reasons why I generally prefer to use the following syntax: package.module.function(). In this case, I will use an alias for the package name, similar to using pd as shorthand for pandas: I will use ptk for pydatk.

I’ll test the syntax I’ve chosen:

$ python
Python 3.13.3 (main, Apr  9 2025, 08:55:03) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pydatk as ptk
>>> ptk.finance.effective_rate(0.06, 12)
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    ptk.finance.effective_rate(0.06, 12)
    ^^^^^^^^^^^
AttributeError: module 'pydatk' has no attribute 'finance'
>>> exit()

The error here (AttributeError: module 'pydatk' has no attribute 'finance') is caused because importing the package only imports src/__init__.py and not the src/finance.py module.

It’s easy to add a reference to the finance module: I’ll add the following line to the top of src/__init__.py: from pydatk import finance. I will need to do this for each additional module.

I’ll retry the previous test:

$ python
Python 3.13.3 (main, Apr  9 2025, 08:55:03) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pydatk as ptk
>>> ptk.finance.effective_rate(0.06, 12)
0.06167781186449828
>>> exit()

The syntax works now, and the function returns the expected result.

Assert

For ad hoc testing, assert is useful to test output against an expected value. In the example below, I’ll demonstrate what happens when the test fails:

$ python
Python 3.13.3 (main, Apr  9 2025, 08:55:03) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pydatk as ptk
>>> assert round(100 * ptk.finance.effective_rate(0.06, 12), 2) == 8
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    assert round(100 * ptk.finance.effective_rate(0.06, 12), 2) == 8
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError
>>> exit()

The next example shows a successful test:

$ python
Python 3.13.3 (main, Apr  9 2025, 08:55:03) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pydatk as ptk
>>> assert round(100 * ptk.finance.effective_rate(0.06, 12), 2) == 6.17
>>> exit()

Summary

In this post, I’ve created the pydatk package’s first feature and tested it using assert in the terminal.

The next post in this series will be about unit testing [3] a Python package.

Feedback or questions?

If you have any feedback or questions I’d love to hear from you. You can comment on posts, use the website and pydatk forums or email me.

References

[1]
[2]
B. Ravi, “Python namespaces & variable scope.” Accessed: Jun. 18, 2025. [Online]. Available: https://www.bhavaniravi.com/blog/python/advanced-python/python-namespaces/
[3]
“Unittest — unit testing framework.” Accessed: Jun. 02, 2025. [Online]. Available: https://docs.python.org/3/library/unittest.html