Skip to content

Commit 3c15dcf

Browse files
Added test which enforces project/module naming/renaming conventions
1 parent 44b1cf9 commit 3c15dcf

9 files changed

Lines changed: 142 additions & 7 deletions

LICENSE

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
MIT License
22

3-
Copyright (c) Microsoft Corporation.
3+
The majority of this repository is copyright (c) Microsoft Corporation.
4+
Minor changes have been made by and are copyright of (c) Tim Littlefair
45

56
Permission is hereby granted, free of charge, to any person obtaining a copy
67
of this software and associated documentation files (the "Software"), to deal

docs/modules.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ src
44
.. toctree::
55
:maxdepth: 4
66

7-
python_package
7+
tl_python_package_template
File renamed without changes.
File renamed without changes.

pyproject.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ requires = ["flit_core >=2,<4"]
33
build-backend = "flit_core.buildapi"
44

55
[project]
6-
name = "py-project-toml"
6+
name = "tl-python-package-template"
77
authors = [
8-
{name = "Daniel Ciborowski", email = "dciborow@microsoft.com"},
8+
# Upstream package was by Daniel Ciborowski up to 2023
9+
# Cloned and updated by Tim Littlefair 2025-
10+
# If anyone else uses this template please ensure these attributes
11+
# are updated before release.
12+
{name = "Tim Littlefair", email = "heretical.camelid@gmail.com"}
913
]
1014
description = "Sample Python Project for creating a new Python Module"
1115
readme = "README.md"
@@ -52,7 +56,7 @@ Source = "https://github.com/microsoft/python-package-template"
5256
Tracker = "https://github.com/microsoft/python-package-template/issues"
5357

5458
[tool.flit.module]
55-
name = "python_package"
59+
name = "tl_python_package_template"
5660

5761
[tool.bandit]
5862
exclude_dirs = ["build","dist","tests","scripts"]
File renamed without changes.
File renamed without changes.

tests/test_methods.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
"""This is a sample python file for testing functions from the source code."""
66
from __future__ import annotations
77

8-
from python_package.hello_world import hello_world
8+
from tl_python_package_template.hello_world import hello_world
99

1010

1111
def hello_test():
1212
"""
1313
This defines the expected usage, which can then be used in various test cases.
14-
Pytest will not execute this code directly, since the function does not contain the suffex "test"
14+
Pytest will not execute this code directly, since the function does not contain the prefix "test"
1515
"""
1616
hello_world()
1717

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# ---------------------------------------------------------------------------------
2+
# Copyright (c) Tim Littlefair. All rights reserved.
3+
# Licensed under the MIT License. See LICENSE in project root for information.
4+
# ---------------------------------------------------------------------------------
5+
6+
"""
7+
This script is part of the tl-python-package-template project.
8+
The project is, as the name suggests, a template from which other
9+
projects can be cloned.
10+
11+
The purpose of this test script is to encourage users who clone
12+
the template as a basis for their own projects, to update the
13+
pyproject.toml file in the base directory and the directory
14+
structure of the project to reflect the name of the project
15+
they are instantiating rather than leaving the placeholder names
16+
in the template.
17+
18+
The tests below make the following assertions:
19+
+ that the basename of the git remote URL matches the name attribute
20+
in pyproject.toml section [project];
21+
+ that there is a subdirectory under src with a name which matches
22+
the name attribute in pyproject.toml section [tool.flit.module]
23+
+ that the name attributes in pyproject.toml [project]; and
24+
[tool.flit.module] agree apart from the conversion of any
25+
'-' (dash) characters in the [project] name into '_' (underscore)
26+
characters in the [tool.flit.module] name.
27+
28+
The names in the base versions of files in the template satisfy
29+
these assertions provided the 'origin' git remote still refers
30+
to https://github.com/tim-littlefair/tl-python-package-template.git,
31+
but when an end-user uses the template as the basis for a repository
32+
with a different basename and clones a sandbox from their new repo,
33+
at least one of the assertions above will fail and will force the
34+
user (preferably) to update pyproject.toml to define their own,
35+
PyPi package name and Python module name where these appear in the
36+
pyproject.toml files and under the src, docs and tests subdirectories
37+
in order to achieve clean-running tests.
38+
Alternatively the end user can delete or modify the source of this
39+
test.
40+
"""
41+
42+
import os
43+
import subprocess
44+
import sys
45+
46+
47+
def _get_name_from_toml_section(toml_section_name):
48+
assert os.path.exists("./pyproject.toml") is True
49+
grep_toml_command = "|".join([
50+
# filter out all lines until we see the section heading
51+
# retain all lines after that point
52+
f"grep -A1 -F '[{toml_section_name}]' pyproject.toml",
53+
# scan for lines after the heading which start with 'name'
54+
"grep -E '^name'",
55+
# accept only the very first line still accepted at this point
56+
"head -1",
57+
# isolate the value, enclosed in double quotes
58+
"""grep -E '"[^"]+"' --only-matching""",
59+
# discard the double quotes
60+
"""sed -e 's/"//g' -"""
61+
])
62+
print(grep_toml_command,file=sys.stderr)
63+
grep_toml_result = subprocess.run(
64+
grep_toml_command,
65+
shell=True, capture_output=True, text=True
66+
)
67+
name_attribute_from_section = grep_toml_result.stdout.strip()
68+
# Uncomment the next two lines to debug the command chain
69+
# assert False, \
70+
# f"Command '{grep_toml_command}' found '{name_attribute_from_sectionUnable to find 'name' attribute section {toml_section_name} in pyproject.toml"
71+
assert len(name_attribute_from_section)>0, \
72+
f"Command '{grep_toml_command}' found '{name_attribute_from_section}' in .toml section '{toml_section_name}'"
73+
print(name_attribute_from_section)
74+
return name_attribute_from_section
75+
76+
_TOML_PROJECT_NAME = _get_name_from_toml_section("project")
77+
_TOML_MODULE_NAME = _get_name_from_toml_section("tool.flit.module")
78+
79+
80+
def test_check_project_name_against_git_remote_origin():
81+
"""
82+
This test requires that the project name in ./pyproject.toml's
83+
[project] section matches the basename of the URL of the default
84+
git remote 'origin', providing that a git remote with that name
85+
exists.
86+
"""
87+
git_remote_result = subprocess.run(
88+
"git remote get-url origin",
89+
shell=True, capture_output=True, text=True
90+
)
91+
origin_repo_basename = os.path.basename(git_remote_result.stdout).strip()
92+
93+
if len(origin_repo_basename)>0:
94+
assert origin_repo_basename.startswith(_TOML_PROJECT_NAME), \
95+
f"Project name in toml file ({_TOML_PROJECT_NAME})" + \
96+
f" does not match prefix of git origin repo URL ({origin_repo_basename})"
97+
print(
98+
f"Project name ({_TOML_PROJECT_NAME})" + \
99+
f" matches URL basename for git remote 'origin' ({origin_repo_basename})"
100+
)
101+
else:
102+
print(
103+
"Project name check skipped because 'git remote get-url origin' returned an empty string"
104+
)
105+
106+
107+
def test_check_module_name_against_src_directory():
108+
"""
109+
This test requires that there is a subdirectory under src which matches
110+
the Python module name in ./pyproject.toml's [tool.flit.module] section.
111+
"""
112+
src_subdirs = os.listdir("./src")
113+
assert _TOML_MODULE_NAME in src_subdirs, f"No subdirectory {_TOML_MODULE_NAME} found under ./src"
114+
115+
116+
def test_check_module_name_against_project_name():
117+
"""
118+
This test requires that the project name in ./pyproject.toml
119+
(which will also be the package name if and when the project is
120+
uploaded to PyPi) matches the name of the Python module it
121+
contains.
122+
If the project name contains '-' (dash) characters, these
123+
are expected to be converted to '_' underscore characters
124+
to generate the Python module name. Other than this t
125+
transliteration, no difference between the names is expected.
126+
"""
127+
assert _TOML_MODULE_NAME == _TOML_PROJECT_NAME.replace("-","_"), \
128+
f"Module name '{_TOML_MODULE_NAME}' does not match project name '{_TOML_PROJECT_NAME}'"
129+
130+

0 commit comments

Comments
 (0)