Skip to content

Commit 8532af8

Browse files
olivermeyerclaude
andauthored
feat(foundry): add python_version_minor computed field (#56)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 9d6c9b1 commit 8532af8

6 files changed

Lines changed: 251 additions & 21 deletions

File tree

ATTRIBUTIONS.md

Lines changed: 205 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
This project includes code from the following third-party open source projects:
66

7-
## Authlib (1.6.9) - BSD License
7+
## Authlib (1.6.11) - BSD License
88

99
The ultimate Python library in building OAuth and OpenID Connect servers and clients.
1010

@@ -360,7 +360,7 @@ SOFTWARE.
360360

361361
```
362362

363-
## aignostics-foundry-core (0.12.0) - MIT License
363+
## aignostics-foundry-core (0.12.1) - MIT License
364364

365365
🏭 Foundational infrastructure for Foundry components.
366366

@@ -6393,7 +6393,7 @@ Python logging made (stupidly) simple
63936393
* URL: https://github.com/Delgan/loguru
63946394
* Author(s): Delgan <delgan.py@gmail.com>
63956395

6396-
## lxml (6.0.2) - BSD-3-Clause
6396+
## lxml (6.1.0) - BSD-3-Clause
63976397

63986398
Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API.
63996399

@@ -7296,7 +7296,7 @@ SOFTWARE.
72967296

72977297
```
72987298

7299-
## pip (26.0.1) - UNKNOWN
7299+
## pip (26.1) - UNKNOWN
73007300

73017301
The PyPA recommended tool for installing Python packages.
73027302

@@ -9966,30 +9966,219 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
99669966

99679967
```
99689968

9969-
## python-multipart (0.0.22) - Apache Software License
9969+
## python-multipart (0.0.26) - Apache Software License
99709970

99719971
A streaming multipart parser for Python
99729972

99739973
* URL: https://github.com/Kludex/python-multipart
9974-
* Author(s): Andrew Dunham <andrew@du.nham.ca>, Marcelo Trylesinski <marcelotryle@gmail.com>
9974+
* Author(s): Andrew Dunham <andrew@du.nham.ca>
9975+
* Maintainer(s): Marcelo Trylesinski <marcelotryle@gmail.com>
99759976

99769977
### License Text
99779978

99789979
```
9979-
Copyright 2012, Andrew Dunham
99809980

9981-
Licensed under the Apache License, Version 2.0 (the "License");
9982-
you may not use this file except in compliance with the License.
9983-
You may obtain a copy of the License at
9981+
Apache License
9982+
Version 2.0, January 2004
9983+
http://www.apache.org/licenses/
99849984

9985-
https://www.apache.org/licenses/LICENSE-2.0
9985+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
99869986

9987-
Unless required by applicable law or agreed to in writing, software
9988-
distributed under the License is distributed on an "AS IS" BASIS,
9989-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9990-
See the License for the specific language governing permissions and
9991-
limitations under the License.
9987+
1. Definitions.
9988+
9989+
"License" shall mean the terms and conditions for use, reproduction,
9990+
and distribution as defined by Sections 1 through 9 of this document.
9991+
9992+
"Licensor" shall mean the copyright owner or entity authorized by
9993+
the copyright owner that is granting the License.
9994+
9995+
"Legal Entity" shall mean the union of the acting entity and all
9996+
other entities that control, are controlled by, or are under common
9997+
control with that entity. For the purposes of this definition,
9998+
"control" means (i) the power, direct or indirect, to cause the
9999+
direction or management of such entity, whether by contract or
10000+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
10001+
outstanding shares, or (iii) beneficial ownership of such entity.
10002+
10003+
"You" (or "Your") shall mean an individual or Legal Entity
10004+
exercising permissions granted by this License.
10005+
10006+
"Source" form shall mean the preferred form for making modifications,
10007+
including but not limited to software source code, documentation
10008+
source, and configuration files.
10009+
10010+
"Object" form shall mean any form resulting from mechanical
10011+
transformation or translation of a Source form, including but
10012+
not limited to compiled object code, generated documentation,
10013+
and conversions to other media types.
10014+
10015+
"Work" shall mean the work of authorship, whether in Source or
10016+
Object form, made available under the License, as indicated by a
10017+
copyright notice that is included in or attached to the work
10018+
(an example is provided in the Appendix below).
10019+
10020+
"Derivative Works" shall mean any work, whether in Source or Object
10021+
form, that is based on (or derived from) the Work and for which the
10022+
editorial revisions, annotations, elaborations, or other modifications
10023+
represent, as a whole, an original work of authorship. For the purposes
10024+
of this License, Derivative Works shall not include works that remain
10025+
separable from, or merely link (or bind by name) to the interfaces of,
10026+
the Work and Derivative Works thereof.
10027+
10028+
"Contribution" shall mean any work of authorship, including
10029+
the original version of the Work and any modifications or additions
10030+
to that Work or Derivative Works thereof, that is intentionally
10031+
submitted to Licensor for inclusion in the Work by the copyright owner
10032+
or by an individual or Legal Entity authorized to submit on behalf of
10033+
the copyright owner. For the purposes of this definition, "submitted"
10034+
means any form of electronic, verbal, or written communication sent
10035+
to the Licensor or its representatives, including but not limited to
10036+
communication on electronic mailing lists, source code control systems,
10037+
and issue tracking systems that are managed by, or on behalf of, the
10038+
Licensor for the purpose of discussing and improving the Work, but
10039+
excluding communication that is conspicuously marked or otherwise
10040+
designated in writing by the copyright owner as "Not a Contribution."
10041+
10042+
"Contributor" shall mean Licensor and any individual or Legal Entity
10043+
on behalf of whom a Contribution has been received by Licensor and
10044+
subsequently incorporated within the Work.
10045+
10046+
2. Grant of Copyright License. Subject to the terms and conditions of
10047+
this License, each Contributor hereby grants to You a perpetual,
10048+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
10049+
copyright license to reproduce, prepare Derivative Works of,
10050+
publicly display, publicly perform, sublicense, and distribute the
10051+
Work and such Derivative Works in Source or Object form.
999210052

10053+
3. Grant of Patent License. Subject to the terms and conditions of
10054+
this License, each Contributor hereby grants to You a perpetual,
10055+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
10056+
(except as stated in this section) patent license to make, have made,
10057+
use, offer to sell, sell, import, and otherwise transfer the Work,
10058+
where such license applies only to those patent claims licensable
10059+
by such Contributor that are necessarily infringed by their
10060+
Contribution(s) alone or by combination of their Contribution(s)
10061+
with the Work to which such Contribution(s) was submitted. If You
10062+
institute patent litigation against any entity (including a
10063+
cross-claim or counterclaim in a lawsuit) alleging that the Work
10064+
or a Contribution incorporated within the Work constitutes direct
10065+
or contributory patent infringement, then any patent licenses
10066+
granted to You under this License for that Work shall terminate
10067+
as of the date such litigation is filed.
10068+
10069+
4. Redistribution. You may reproduce and distribute copies of the
10070+
Work or Derivative Works thereof in any medium, with or without
10071+
modifications, and in Source or Object form, provided that You
10072+
meet the following conditions:
10073+
10074+
(a) You must give any other recipients of the Work or
10075+
Derivative Works a copy of this License; and
10076+
10077+
(b) You must cause any modified files to carry prominent notices
10078+
stating that You changed the files; and
10079+
10080+
(c) You must retain, in the Source form of any Derivative Works
10081+
that You distribute, all copyright, patent, trademark, and
10082+
attribution notices from the Source form of the Work,
10083+
excluding those notices that do not pertain to any part of
10084+
the Derivative Works; and
10085+
10086+
(d) If the Work includes a "NOTICE" text file as part of its
10087+
distribution, then any Derivative Works that You distribute must
10088+
include a readable copy of the attribution notices contained
10089+
within such NOTICE file, excluding those notices that do not
10090+
pertain to any part of the Derivative Works, in at least one
10091+
of the following places: within a NOTICE text file distributed
10092+
as part of the Derivative Works; within the Source form or
10093+
documentation, if provided along with the Derivative Works; or,
10094+
within a display generated by the Derivative Works, if and
10095+
wherever such third-party notices normally appear. The contents
10096+
of the NOTICE file are for informational purposes only and
10097+
do not modify the License. You may add Your own attribution
10098+
notices within Derivative Works that You distribute, alongside
10099+
or as an addendum to the NOTICE text from the Work, provided
10100+
that such additional attribution notices cannot be construed
10101+
as modifying the License.
10102+
10103+
You may add Your own copyright statement to Your modifications and
10104+
may provide additional or different license terms and conditions
10105+
for use, reproduction, or distribution of Your modifications, or
10106+
for any such Derivative Works as a whole, provided Your use,
10107+
reproduction, and distribution of the Work otherwise complies with
10108+
the conditions stated in this License.
10109+
10110+
5. Submission of Contributions. Unless You explicitly state otherwise,
10111+
any Contribution intentionally submitted for inclusion in the Work
10112+
by You to the Licensor shall be under the terms and conditions of
10113+
this License, without any additional terms or conditions.
10114+
Notwithstanding the above, nothing herein shall supersede or modify
10115+
the terms of any separate license agreement you may have executed
10116+
with Licensor regarding such Contributions.
10117+
10118+
6. Trademarks. This License does not grant permission to use the trade
10119+
names, trademarks, service marks, or product names of the Licensor,
10120+
except as required for reasonable and customary use in describing the
10121+
origin of the Work and reproducing the content of the NOTICE file.
10122+
10123+
7. Disclaimer of Warranty. Unless required by applicable law or
10124+
agreed to in writing, Licensor provides the Work (and each
10125+
Contributor provides its Contributions) on an "AS IS" BASIS,
10126+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
10127+
implied, including, without limitation, any warranties or conditions
10128+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
10129+
PARTICULAR PURPOSE. You are solely responsible for determining the
10130+
appropriateness of using or redistributing the Work and assume any
10131+
risks associated with Your exercise of permissions under this License.
10132+
10133+
8. Limitation of Liability. In no event and under no legal theory,
10134+
whether in tort (including negligence), contract, or otherwise,
10135+
unless required by applicable law (such as deliberate and grossly
10136+
negligent acts) or agreed to in writing, shall any Contributor be
10137+
liable to You for damages, including any direct, indirect, special,
10138+
incidental, or consequential damages of any character arising as a
10139+
result of this License or out of the use or inability to use the
10140+
Work (including but not limited to damages for loss of goodwill,
10141+
work stoppage, computer failure or malfunction, or any and all
10142+
other commercial damages or losses), even if such Contributor
10143+
has been advised of the possibility of such damages.
10144+
10145+
9. Accepting Warranty or Additional Liability. While redistributing
10146+
the Work or Derivative Works thereof, You may choose to offer,
10147+
and charge a fee for, acceptance of support, warranty, indemnity,
10148+
or other liability obligations and/or rights consistent with this
10149+
License. However, in accepting such obligations, You may act only
10150+
on Your own behalf and on Your sole responsibility, not on behalf
10151+
of any other Contributor, and only if You agree to indemnify,
10152+
defend, and hold each Contributor harmless for any liability
10153+
incurred by, or claims asserted against, such Contributor by reason
10154+
of your accepting any such warranty or additional liability.
10155+
10156+
END OF TERMS AND CONDITIONS
10157+
10158+
APPENDIX: How to apply the Apache License to your work.
10159+
10160+
To apply the Apache License to your work, attach the following
10161+
boilerplate notice, with the fields enclosed by brackets "[]"
10162+
replaced with your own identifying information. (Don't include
10163+
the brackets!) The text should be enclosed in the appropriate
10164+
comment syntax for the file format. We also recommend that a
10165+
file or class name and description of purpose be included on the
10166+
same "printed page" as the copyright notice for easier
10167+
identification within third-party archives.
10168+
10169+
Copyright [yyyy] [name of copyright owner]
10170+
10171+
Licensed under the Apache License, Version 2.0 (the "License");
10172+
you may not use this file except in compliance with the License.
10173+
You may obtain a copy of the License at
10174+
10175+
http://www.apache.org/licenses/LICENSE-2.0
10176+
10177+
Unless required by applicable law or agreed to in writing, software
10178+
distributed under the License is distributed on an "AS IS" BASIS,
10179+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10180+
See the License for the specific language governing permissions and
10181+
limitations under the License.
999310182

999410183
```
999510184

src/aignostics_foundry_core/AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ This file provides an overview of all modules in `aignostics_foundry_core`, thei
5050
Defaults to empty values for direct construction (e.g. in tests).
5151
- `FoundryContext(BaseModel)` — frozen; fields: `name`, `version`, `version_full`, `version_with_vcs_ref`, `environment`,
5252
`env_file: list[Path]`, `env_prefix`, `python_version` (Python runtime version string,
53-
e.g. `"3.11.9"`), `metadata: PackageMetadata` (all package-derived fields: description,
53+
e.g. `"3.11.9"`), `python_version_minor` (computed; major and minor only, e.g. `"3.11"`), `metadata: PackageMetadata` (all package-derived fields: description,
5454
author, URLs; populated by `from_package()` via `PackageMetadata.from_name()`; defaults to
5555
empty `PackageMetadata()`), plus four runtime mode bool flags: `is_container`, `is_cli`,
5656
`is_test`, `is_library` (all default `False`), and `database: DatabaseSettings | None`

src/aignostics_foundry_core/foundry.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from pathlib import Path
2929

3030
from dotenv import dotenv_values
31-
from pydantic import BaseModel, Field
31+
from pydantic import BaseModel, Field, computed_field
3232

3333
from aignostics_foundry_core.database import DatabaseSettings
3434

@@ -127,6 +127,21 @@ class FoundryContext(BaseModel):
127127
is_test: bool = False
128128
is_library: bool = False
129129
python_version: str = ""
130+
131+
@computed_field
132+
@property
133+
def python_version_minor(self) -> str:
134+
"""Python runtime version limited to major and minor components (e.g. ``'3.11'``).
135+
136+
Derived from :attr:`python_version`. Returns ``""`` when :attr:`python_version`
137+
is not set (e.g. when the context is constructed directly in tests without
138+
specifying a version).
139+
"""
140+
if not self.python_version:
141+
return ""
142+
major_minor_part_count = 2
143+
return ".".join(self.python_version.split(".")[:major_minor_part_count])
144+
130145
project_path: Path | None = None
131146
metadata: PackageMetadata = Field(default_factory=PackageMetadata)
132147
"""Package-derived author and description metadata.

tests/aignostics_foundry_core/foundry_test.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,28 @@ def test_from_package_python_version_matches_platform() -> None:
120120
assert ctx.python_version == platform.python_version()
121121

122122

123+
@pytest.mark.unit
124+
def test_foundry_context_python_version_minor_from_full_version() -> None:
125+
"""python_version_minor returns major.minor when python_version is a full x.y.z string."""
126+
ctx = make_context(python_version="3.11.9")
127+
assert ctx.python_version_minor == "3.11"
128+
129+
130+
@pytest.mark.unit
131+
def test_foundry_context_python_version_minor_empty_when_version_unset() -> None:
132+
"""python_version_minor returns '' when python_version is not set."""
133+
ctx = make_context()
134+
assert not ctx.python_version_minor
135+
136+
137+
@pytest.mark.unit
138+
def test_foundry_context_python_version_minor_from_package() -> None:
139+
"""from_package() produces python_version_minor consistent with platform.python_version()."""
140+
ctx = FoundryContext.from_package(PACKAGE_NAME)
141+
expected = ".".join(platform.python_version().split(".")[:2])
142+
assert ctx.python_version_minor == expected
143+
144+
123145
@pytest.mark.unit
124146
def test_from_package_returns_correct_name() -> None:
125147
"""from_package() sets .name to the package_name argument."""

tests/conftest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def make_context( # noqa: PLR0913
8585
database: DatabaseSettings | None = None,
8686
env_file: list[Path] | None = None,
8787
metadata: PackageMetadata | None = None,
88+
python_version: str = "",
8889
**kwargs: bool,
8990
) -> FoundryContext:
9091
"""Create a minimal FoundryContext for testing.
@@ -103,6 +104,8 @@ def make_context( # noqa: PLR0913
103104
metadata: Optional :class:`~aignostics_foundry_core.foundry.PackageMetadata`
104105
to attach to the context. When ``None``, a ``PackageMetadata`` with
105106
``repository_url`` set from the *repository_url* argument is used.
107+
python_version: Python runtime version string (e.g. ``"3.11.9"``). Defaults to
108+
``""`` so contexts constructed in tests work without specifying a version.
106109
**kwargs: Optional boolean flags forwarded to :class:`FoundryContext`
107110
(``is_test``, ``is_cli``, ``is_container``, ``is_library``).
108111
"""
@@ -118,5 +121,6 @@ def make_context( # noqa: PLR0913
118121
database=database,
119122
env_file=env_file or [],
120123
metadata=resolved_metadata,
124+
python_version=python_version,
121125
**kwargs, # type: ignore[arg-type]
122126
)

uv.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)