0
# Project Utilities
1
2
Extract project metadata like version and name from packages for automatic changelog generation.
3
4
## Capabilities
5
6
### Version Detection
7
8
Get the version of a package from metadata or source code.
9
10
```python { .api }
11
def get_version(package_dir: str, package: str) -> str:
12
"""
13
Get the version of a package.
14
15
Try to extract the version from the distribution version metadata that matches
16
`package`, then fall back to looking for the package in `package_dir`.
17
18
Args:
19
package_dir: Directory containing the package source
20
package: Package name to get version for
21
22
Returns:
23
str: Package version string
24
25
Raises:
26
ImportError: If package cannot be imported
27
AttributeError: If version cannot be determined
28
"""
29
```
30
31
### Project Name Detection
32
33
Get the project name from package metadata.
34
35
```python { .api }
36
def get_project_name(package_dir: str, package: str) -> str:
37
"""
38
Get the project name from package metadata.
39
40
Args:
41
package_dir: Directory containing the package source
42
package: Package name to get project name for
43
44
Returns:
45
str: Project name from metadata
46
47
Raises:
48
ImportError: If package cannot be imported
49
AttributeError: If project name cannot be determined
50
"""
51
```
52
53
### Internal Utilities
54
55
Helper functions for package loading and metadata access.
56
57
```python { .api }
58
def _get_package(package_dir: str, package: str) -> ModuleType:
59
"""
60
Import a package from a specific directory.
61
62
First tries to import normally, then adds package_dir to sys.path
63
and tries again if the initial import fails.
64
65
Args:
66
package_dir: Directory to search for package
67
package: Package name to import
68
69
Returns:
70
ModuleType: Imported package module
71
72
Raises:
73
ImportError: If package cannot be imported from any location
74
"""
75
76
def _get_metadata_version(package: str) -> str | None:
77
"""
78
Try to get the version from package distribution metadata.
79
80
Args:
81
package: Package name to look up
82
83
Returns:
84
str | None: Version string if found, None otherwise
85
"""
86
```
87
88
## Usage Examples
89
90
### Basic Version Detection
91
92
```python
93
from towncrier._project import get_version, get_project_name
94
95
# Get version from installed package metadata first, then source
96
version = get_version(
97
package_dir="src",
98
package="myproject"
99
)
100
print(f"Version: {version}")
101
102
# Get project name
103
project_name = get_project_name(
104
package_dir="src",
105
package="myproject"
106
)
107
print(f"Project: {project_name}")
108
```
109
110
### Integration with Build Process
111
112
```python
113
from towncrier._project import get_version
114
from towncrier._settings import load_config_from_options
115
116
# Load config and get version if not specified
117
base_directory, config = load_config_from_options(None, None)
118
119
if not config.version and config.package:
120
# Auto-detect version from package
121
detected_version = get_version(
122
package_dir=os.path.join(base_directory, config.package_dir),
123
package=config.package
124
)
125
print(f"Detected version: {detected_version}")
126
```
127
128
### Package Import Handling
129
130
```python
131
from towncrier._project import _get_package
132
133
try:
134
# Import package from source directory
135
module = _get_package(
136
package_dir="/path/to/src",
137
package="mypackage"
138
)
139
140
# Access package attributes
141
if hasattr(module, '__version__'):
142
version = module.__version__
143
144
except ImportError as e:
145
print(f"Cannot import package: {e}")
146
```
147
148
## Version Resolution Strategy
149
150
The `get_version` function uses this resolution order:
151
152
1. **Distribution metadata**: Check installed package metadata using `importlib.metadata.version()`
153
2. **Package __version__**: Import package and check `__version__` attribute
154
3. **Package _version**: Import package and check `_version` attribute
155
4. **Package VERSION**: Import package and check `VERSION` attribute
156
5. **Package version**: Import package and check `version` attribute
157
158
## Project Name Resolution
159
160
The `get_project_name` function uses:
161
162
1. **Distribution metadata**: Check installed package metadata using `importlib.metadata.metadata()`
163
2. **Package __name__**: Fall back to package name itself
164
165
## Error Handling
166
167
Project utilities handle these error scenarios:
168
169
- **ImportError**: Package cannot be imported from any location
170
- **PackageNotFoundError**: Package not found in distribution metadata
171
- **AttributeError**: Required version/name attributes not found
172
- **ModuleNotFoundError**: Package module structure issues
173
- **Path errors**: Invalid package_dir or package paths
174
175
## Integration Points
176
177
These utilities are used by:
178
179
- **Build command**: Auto-detect project version when not specified in config
180
- **Configuration system**: Resolve package-based settings
181
- **Template rendering**: Provide project metadata to templates
182
- **CLI commands**: Display project information in help and output