Skip to content

Commit 5e51565

Browse files
authored
Merge branch 'master' into python-mutable-immutable
2 parents 8b8caed + 9b70523 commit 5e51565

File tree

40 files changed

+14013
-0
lines changed

40 files changed

+14013
-0
lines changed

namespace-package/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# What's a Python Namespace Package, and What's It For?
2+
3+
Materials for the corresponding Real Python tutorial, [What's a Python Namespace Package, and What's It For?](https://realpython.com/python-namespace-package/).
4+
5+
You have the source code for the Snake Corporation example that's hosted on PyPI. Aditionally, as an extra, you have the code to generate the fake data used.
6+
7+
You need to install every subpackage to get the code working properly.
8+
9+
## Examples
10+
11+
In this repository, you'll find three different examples showing ways that namespace packages work or can be used.
12+
13+
### Business
14+
15+
A typical use case of namespace packages is for large businesses that want to make sure all their code is packaged under a namespace. In this and subsequent examples, you play the role of the fictional Snake Corporation.
16+
17+
For this example, the Snake Corporation has two separate utility packages, but when both are installed, both are available under the same `snake_corp` namespace:
18+
19+
```py
20+
>>> from snake_corp import dateutil
21+
>>> from snake_corp import magic
22+
```
23+
24+
### Distributed Data Repository
25+
26+
In this example, you make a namespace package that serves as a wrapper around your data files. This allows you to create separate packages that only contain data.
27+
28+
This way, an organization like Real Python can have some core functionality in the main package, like the functions to read the data, and then have many satellite data packages that just include a particular dataset and insert it into the namespace so it can be read by the core package.
29+
30+
### Plugin System
31+
32+
In this example, you take the data repository example further by adding a plugin system for the readers so that you can support your own custom formats.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[tool.black]
2+
line-length=79
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[build-system]
2+
requires = ["setuptools", "setuptools-scm"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[tool.setuptools.packages.find]
6+
where = ["."]
7+
include = ["snake_corp"]
8+
namespaces = true
9+
10+
[project]
11+
name = "snake-corp-dateutil"
12+
version = "1.0.3"
13+
description = "Date Utilities for the Snake Corporation"
14+
license = { text = "Snake Corp Software License Agreement" }
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from datetime import datetime
2+
3+
4+
def days_to_snake_day(from_date=None):
5+
"""Return the number of days until World Snake Day (July 16th)."""
6+
if from_date is None:
7+
from_date = datetime.now()
8+
if from_date.month > 7 or (from_date.month == 7 and from_date.day > 16):
9+
day_of_snake = datetime(from_date.year + 1, 7, 16)
10+
else:
11+
day_of_snake = datetime(from_date.year, 7, 16)
12+
return (day_of_snake - from_date).days
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[build-system]
2+
requires = ["setuptools", "setuptools-scm"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[tool.setuptools.packages.find]
6+
where = ["."]
7+
include = ["snake_corp"]
8+
namespaces = true
9+
10+
[project]
11+
name = "snake-corp-magic-numbers"
12+
version = "1.1.8"
13+
description = "Super Magic Secret Numbers for the Snake Corporation"
14+
license = { text = "Snake Corp Software License Agreement" }
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import random
2+
3+
4+
def secret_number_generator():
5+
# Insert special Snake Corporation sauce here ...
6+
return random.randint(0, 9999)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from snake_corp import dateutil, magic
2+
3+
print(magic.secret_number_generator())
4+
print(dateutil.days_to_snake_day())
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
```s
2+
$ python -m venv venv
3+
$ source venv/bin/activate
4+
(venv) $ python -m pip install -e data-repos/
5+
(venv) $ python -m pip install -e snake-corp-product-data/
6+
```
7+
8+
```powershell
9+
PS> python -m venv venv
10+
PS> venv\scripts\activate
11+
(venv) PS> python -m pip install -e "data-repos\"
12+
(venv) PS> python -m pip install -e "snake-corp-product-data\"
13+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from data_repos import read
2+
3+
read.data("products")
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from importlib import resources
2+
3+
import pandas as pd
4+
5+
6+
def data(name):
7+
"""Get a data file."""
8+
data_path = path(name)
9+
file_type = data_path.suffix.lstrip(".")
10+
return readers[file_type](data_path)
11+
12+
13+
def path(name):
14+
"""Find the path to a data file."""
15+
for resource in resources.files(__package__).iterdir():
16+
if resource.stem == name:
17+
return resource
18+
raise FileNotFoundError(f"{name} not found in {__package__}")
19+
20+
21+
def csv(data_path):
22+
"""Read a CSV file from a path."""
23+
return pd.read_csv(data_path)
24+
25+
26+
def json(data_path):
27+
"""Read a JSON file from a path."""
28+
return pd.read_json(data_path)
29+
30+
31+
readers = {"csv": csv, "json": json}

0 commit comments

Comments
 (0)