From e5c0777b32d839b2b7d244361d5696d71d995555 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Mon, 9 Mar 2026 10:09:51 -0700 Subject: [PATCH 1/8] dependency update + strict --- dev_requirements.txt | 6 ++-- featuremanagement/__init__.py | 1 + featuremanagement/_defaultfilters.py | 1 + featuremanagement/_featurefilters.py | 1 + featuremanagement/_featuremanager.py | 1 + featuremanagement/_featuremanagerbase.py | 1 + featuremanagement/_models/__init__.py | 1 + featuremanagement/_models/_allocation.py | 1 + featuremanagement/_models/_constants.py | 1 + .../_models/_evaluation_event.py | 1 + .../_models/_feature_conditions.py | 1 + featuremanagement/_models/_feature_flag.py | 1 + .../_models/_targeting_context.py | 1 + featuremanagement/_models/_telemetry.py | 1 + featuremanagement/_models/_variant.py | 1 + .../_models/_variant_assignment_reason.py | 1 + .../_models/_variant_reference.py | 1 + .../_time_window_filter/__init__.py | 1 + .../_time_window_filter/_models.py | 1 + .../_recurrence_evaluator.py | 1 + .../_recurrence_validator.py | 1 + featuremanagement/_version.py | 1 + featuremanagement/aio/__init__.py | 1 + featuremanagement/aio/_defaultfilters.py | 1 + featuremanagement/aio/_featurefilters.py | 1 + featuremanagement/aio/_featuremanager.py | 1 + featuremanagement/azuremonitor/__init__.py | 1 + .../azuremonitor/_send_telemetry.py | 1 + mypy.ini | 29 +------------------ pyproject.toml | 2 +- 30 files changed, 32 insertions(+), 32 deletions(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index 1411462..8a4f4b9 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,12 +1,12 @@ -pytest < 9.0.0 +pytest < 10.0.0 pytest-cov < 8.0.0 pytest-asyncio < 2.0.0 black < 27.0.0 pylint < 5.0.0 mypy < 2.0.0 -sphinx < 9.0.0 +sphinx < 10.0.0 sphinx_rtd_theme < 4.0.0 sphinx-toolbox < 5.0.0 -myst_parser < 5.0.0 +myst_parser < 6.0.0 opentelemetry-api < 2.0.0 opentelemetry-sdk < 2.0.0 diff --git a/featuremanagement/__init__.py b/featuremanagement/__init__.py index 0a06a1c..adc0bee 100644 --- a/featuremanagement/__init__.py +++ b/featuremanagement/__init__.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Feature management library for Python.""" from ._featuremanager import FeatureManager from ._featurefilters import FeatureFilter from ._defaultfilters import TimeWindowFilter, TargetingFilter diff --git a/featuremanagement/_defaultfilters.py b/featuremanagement/_defaultfilters.py index 75b8b7a..4c529af 100644 --- a/featuremanagement/_defaultfilters.py +++ b/featuremanagement/_defaultfilters.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Built-in feature filter implementations.""" import logging import hashlib from datetime import datetime, timezone diff --git a/featuremanagement/_featurefilters.py b/featuremanagement/_featurefilters.py index c8a5021..ff685ea 100644 --- a/featuremanagement/_featurefilters.py +++ b/featuremanagement/_featurefilters.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Base class for feature filters.""" from abc import ABC, abstractmethod from typing import Mapping, Callable, Any, Optional diff --git a/featuremanagement/_featuremanager.py b/featuremanagement/_featuremanager.py index 49a4cf6..ca33c81 100644 --- a/featuremanagement/_featuremanager.py +++ b/featuremanagement/_featuremanager.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Synchronous feature manager implementation.""" import logging from typing import cast, overload, Any, Optional, Dict, Mapping, List, Tuple from ._defaultfilters import TimeWindowFilter, TargetingFilter diff --git a/featuremanagement/_featuremanagerbase.py b/featuremanagement/_featuremanagerbase.py index d79021f..7dc109a 100644 --- a/featuremanagement/_featuremanagerbase.py +++ b/featuremanagement/_featuremanagerbase.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Base class for feature manager implementations.""" import hashlib import logging from abc import ABC diff --git a/featuremanagement/_models/__init__.py b/featuremanagement/_models/__init__.py index ef38d49..e50022f 100644 --- a/featuremanagement/_models/__init__.py +++ b/featuremanagement/_models/__init__.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Data models for feature management.""" from ._feature_flag import FeatureFlag from ._variant import Variant from ._evaluation_event import EvaluationEvent diff --git a/featuremanagement/_models/_allocation.py b/featuremanagement/_models/_allocation.py index 09dba6a..6089040 100644 --- a/featuremanagement/_models/_allocation.py +++ b/featuremanagement/_models/_allocation.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Allocation model for feature variant assignment.""" from typing import cast, List, Optional, Mapping, Dict, Any, Union from dataclasses import dataclass from ._constants import DEFAULT_WHEN_ENABLED, DEFAULT_WHEN_DISABLED, USER, GROUP, PERCENTILE, SEED diff --git a/featuremanagement/_models/_constants.py b/featuremanagement/_models/_constants.py index dd5ea35..a67e1d3 100644 --- a/featuremanagement/_models/_constants.py +++ b/featuremanagement/_models/_constants.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Constants used by feature management models.""" # Feature Flag FEATURE_FLAG_ID = "id" diff --git a/featuremanagement/_models/_evaluation_event.py b/featuremanagement/_models/_evaluation_event.py index c0d7827..23049d0 100644 --- a/featuremanagement/_models/_evaluation_event.py +++ b/featuremanagement/_models/_evaluation_event.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Evaluation event model for feature flag telemetry.""" from dataclasses import dataclass from typing import Optional from ._feature_flag import FeatureFlag diff --git a/featuremanagement/_models/_feature_conditions.py b/featuremanagement/_models/_feature_conditions.py index 2d2c276..563b4ff 100644 --- a/featuremanagement/_models/_feature_conditions.py +++ b/featuremanagement/_models/_feature_conditions.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Feature flag condition models.""" from collections.abc import Mapping from typing import Any, Dict, List from ._constants import ( diff --git a/featuremanagement/_models/_feature_flag.py b/featuremanagement/_models/_feature_flag.py index e0665c1..d0db05a 100644 --- a/featuremanagement/_models/_feature_flag.py +++ b/featuremanagement/_models/_feature_flag.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Feature flag model.""" from typing import cast, List, Union, Optional, Mapping, Any from ._feature_conditions import FeatureConditions from ._allocation import Allocation diff --git a/featuremanagement/_models/_targeting_context.py b/featuremanagement/_models/_targeting_context.py index 8aa1cba..7f9ee19 100644 --- a/featuremanagement/_models/_targeting_context.py +++ b/featuremanagement/_models/_targeting_context.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Targeting context model for user and group targeting.""" from typing import NamedTuple, List diff --git a/featuremanagement/_models/_telemetry.py b/featuremanagement/_models/_telemetry.py index c54ad84..2225f8c 100644 --- a/featuremanagement/_models/_telemetry.py +++ b/featuremanagement/_models/_telemetry.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Telemetry metadata model.""" from typing import Dict from dataclasses import dataclass, field diff --git a/featuremanagement/_models/_variant.py b/featuremanagement/_models/_variant.py index 382ada3..93458fb 100644 --- a/featuremanagement/_models/_variant.py +++ b/featuremanagement/_models/_variant.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Variant model representing a feature flag variant.""" from typing import Any diff --git a/featuremanagement/_models/_variant_assignment_reason.py b/featuremanagement/_models/_variant_assignment_reason.py index 5e8c2c7..06d2b0d 100644 --- a/featuremanagement/_models/_variant_assignment_reason.py +++ b/featuremanagement/_models/_variant_assignment_reason.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Enum for variant assignment reasons.""" from enum import Enum diff --git a/featuremanagement/_models/_variant_reference.py b/featuremanagement/_models/_variant_reference.py index 893c09e..28a1679 100644 --- a/featuremanagement/_models/_variant_reference.py +++ b/featuremanagement/_models/_variant_reference.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Variant reference model.""" from dataclasses import dataclass from typing import Optional, Mapping, Any from ._constants import VARIANT_REFERENCE_NAME, CONFIGURATION_VALUE, STATUS_OVERRIDE diff --git a/featuremanagement/_time_window_filter/__init__.py b/featuremanagement/_time_window_filter/__init__.py index 34290bd..c955697 100644 --- a/featuremanagement/_time_window_filter/__init__.py +++ b/featuremanagement/_time_window_filter/__init__.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Time window filter with recurrence support.""" from ._recurrence_evaluator import is_match from ._models import Recurrence, TimeWindowFilterSettings diff --git a/featuremanagement/_time_window_filter/_models.py b/featuremanagement/_time_window_filter/_models.py index ff3574b..126e08a 100644 --- a/featuremanagement/_time_window_filter/_models.py +++ b/featuremanagement/_time_window_filter/_models.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Data models for the time window filter.""" from enum import Enum from typing import Dict, Any, Optional, List from datetime import datetime diff --git a/featuremanagement/_time_window_filter/_recurrence_evaluator.py b/featuremanagement/_time_window_filter/_recurrence_evaluator.py index 9034b89..4079ba5 100644 --- a/featuremanagement/_time_window_filter/_recurrence_evaluator.py +++ b/featuremanagement/_time_window_filter/_recurrence_evaluator.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Recurrence evaluation logic for the time window filter.""" from datetime import datetime, timedelta from typing import Optional from ._models import RecurrencePatternType, RecurrenceRangeType, TimeWindowFilterSettings, OccurrenceInfo, Recurrence diff --git a/featuremanagement/_time_window_filter/_recurrence_validator.py b/featuremanagement/_time_window_filter/_recurrence_validator.py index 8c34aaa..4f3cc06 100644 --- a/featuremanagement/_time_window_filter/_recurrence_validator.py +++ b/featuremanagement/_time_window_filter/_recurrence_validator.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Validation logic for recurrence settings.""" from datetime import datetime, timedelta from typing import List from ._models import RecurrencePatternType, RecurrenceRangeType, Recurrence, RecurrencePattern, RecurrenceRange diff --git a/featuremanagement/_version.py b/featuremanagement/_version.py index bade305..61b4d08 100644 --- a/featuremanagement/_version.py +++ b/featuremanagement/_version.py @@ -3,5 +3,6 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Package version.""" VERSION = "2.1.0" diff --git a/featuremanagement/aio/__init__.py b/featuremanagement/aio/__init__.py index 34c5471..4c7a107 100644 --- a/featuremanagement/aio/__init__.py +++ b/featuremanagement/aio/__init__.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Async feature management support.""" from ._featuremanager import FeatureManager from ._featurefilters import FeatureFilter from ._defaultfilters import TimeWindowFilter, TargetingFilter diff --git a/featuremanagement/aio/_defaultfilters.py b/featuremanagement/aio/_defaultfilters.py index ab8c1d8..26572fc 100644 --- a/featuremanagement/aio/_defaultfilters.py +++ b/featuremanagement/aio/_defaultfilters.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Built-in async feature filter implementations.""" from typing import Mapping, Any from ._featurefilters import FeatureFilter from .._defaultfilters import ( diff --git a/featuremanagement/aio/_featurefilters.py b/featuremanagement/aio/_featurefilters.py index 5305eaf..d53d5a3 100644 --- a/featuremanagement/aio/_featurefilters.py +++ b/featuremanagement/aio/_featurefilters.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Base class for async feature filters.""" from abc import ABC, abstractmethod from typing import Mapping, Callable, Any, Optional diff --git a/featuremanagement/aio/_featuremanager.py b/featuremanagement/aio/_featuremanager.py index d5f45a7..6513652 100644 --- a/featuremanagement/aio/_featuremanager.py +++ b/featuremanagement/aio/_featuremanager.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Async feature manager implementation.""" import inspect import logging from typing import cast, overload, Any, Optional, Dict, Mapping, List, Tuple diff --git a/featuremanagement/azuremonitor/__init__.py b/featuremanagement/azuremonitor/__init__.py index 135fdf7..a2fdbae 100644 --- a/featuremanagement/azuremonitor/__init__.py +++ b/featuremanagement/azuremonitor/__init__.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Azure Monitor telemetry integration for feature management.""" from ._send_telemetry import publish_telemetry, track_event, TargetingSpanProcessor __all__ = [ diff --git a/featuremanagement/azuremonitor/_send_telemetry.py b/featuremanagement/azuremonitor/_send_telemetry.py index 0a7f727..55187b4 100644 --- a/featuremanagement/azuremonitor/_send_telemetry.py +++ b/featuremanagement/azuremonitor/_send_telemetry.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Telemetry publishing for feature evaluation events.""" import logging import inspect from typing import Any, Callable, Dict, Optional diff --git a/mypy.ini b/mypy.ini index 0a01c74..f24c551 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,31 +1,4 @@ [mypy] python_version = 3.10 -# Start off with these -warn_unused_configs = True -warn_redundant_casts = True -warn_unused_ignores = True - -# Getting these passing should be easy -strict_equality = True -extra_checks = True - -# Strongly recommend enabling this one as soon as you can -check_untyped_defs = True - -# These shouldn't be too much additional work, but may be tricky to -# get passing if you use a lot of untyped libraries -disallow_subclassing_any = True -disallow_untyped_decorators = True -disallow_any_generics = True - -# These next few are various gradations of forcing use of type annotations -disallow_untyped_calls = True -disallow_incomplete_defs = True -disallow_untyped_defs = True - -# This one isn't too hard to get passing, but return on investment is lower -no_implicit_reexport = True - -# This one can be tricky to get passing if you use a lot of untyped libraries -warn_return_any = True +strict = True diff --git a/pyproject.toml b/pyproject.toml index d160c35..f2a5d4e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ max-line-length = 120 min-public-methods = 1 max-branches = 20 max-returns = 7 -disable = ["missing-module-docstring", "duplicate-code"] +disable = ["duplicate-code"] [build-system] requires = ["setuptools>=61.0", "pylint", "pytest-asyncio", "mypy", "black"] From 429fff3ccb04b5477fbdd25d0a906f5bb94a68de Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Mon, 9 Mar 2026 10:31:16 -0700 Subject: [PATCH 2/8] removing setup py usage --- .github/workflows/validate.yml | 5 ++- MANIFEST.in | 6 ---- cspell.config.yaml | 2 +- dev_requirements.txt | 12 ------- pyproject.toml | 34 ++++++++++++++++-- setup.py | 64 ---------------------------------- tests/requirements.txt | 2 -- 7 files changed, 34 insertions(+), 91 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 dev_requirements.txt delete mode 100644 setup.py delete mode 100644 tests/requirements.txt diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 730bc75..b7825f0 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -17,8 +17,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r dev_requirements.txt - pip install . + pip install ".[dev]" - name: Analysing the code with pylint run: | pylint featuremanagement @@ -30,7 +29,7 @@ jobs: uses: streetsidesoftware/cspell-action@v6.8.0 - name: Test with pytest run: | - pip install -r tests/requirements.txt + pip install ".[test]" pytest tests --doctest-modules --cov-report=xml --cov-report=html - name: Analysing the samples with pylint run: | diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index bfa9241..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -recursive-include tests *.py -include *.md -include LICENSE -include featuremanagement/*.py -include featuremanagement/aio/*.py -recursive-include samples *.py *.json diff --git a/cspell.config.yaml b/cspell.config.yaml index 7d80bb8..5777535 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -12,7 +12,7 @@ ignorePaths: - '.*' - 'build' - 'docs' - - 'dev_requirements.txt' + - 'node_modules' - '*.egg-info' - '*.ini' - '*.toml' diff --git a/dev_requirements.txt b/dev_requirements.txt deleted file mode 100644 index 8a4f4b9..0000000 --- a/dev_requirements.txt +++ /dev/null @@ -1,12 +0,0 @@ -pytest < 10.0.0 -pytest-cov < 8.0.0 -pytest-asyncio < 2.0.0 -black < 27.0.0 -pylint < 5.0.0 -mypy < 2.0.0 -sphinx < 10.0.0 -sphinx_rtd_theme < 4.0.0 -sphinx-toolbox < 5.0.0 -myst_parser < 6.0.0 -opentelemetry-api < 2.0.0 -opentelemetry-sdk < 2.0.0 diff --git a/pyproject.toml b/pyproject.toml index f2a5d4e..f0fe2ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,20 +6,24 @@ pythonpath = [ [tool.black] line-length = 120 -[tool.pylint] +[tool.pylint.format] max-line-length = 120 + +[tool.pylint.design] min-public-methods = 1 max-branches = 20 max-returns = 7 + +[tool.pylint."messages control"] disable = ["duplicate-code"] [build-system] -requires = ["setuptools>=61.0", "pylint", "pytest-asyncio", "mypy", "black"] +requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" [project] name = "FeatureManagement" -version = "2.1.0" +dynamic = ["version"] authors = [ { name="Microsoft Corporation", email="appconfig@microsoft.com" }, ] @@ -40,9 +44,33 @@ classifiers = [ "License :: OSI Approved :: MIT License", ] +[tool.setuptools.dynamic] +version = {attr = "featuremanagement._version.VERSION"} + +[tool.setuptools.packages.find] +include = ["featuremanagement*"] + [project.urls] Homepage = "https://github.com/microsoft/FeatureManagement-Python" Issues = "https://github.com/microsoft/FeatureManagement-Python/issues" [project.optional-dependencies] AzureMonitor = ["azure-monitor-events-extension<2.0.0"] +test = [ + "azure-monitor-opentelemetry", + "azure-monitor-events-extension", +] +dev = [ + "pytest >= 7.0.0, < 10.0.0", + "pytest-cov >= 4.0.0, < 8.0.0", + "pytest-asyncio >= 0.21.0, < 2.0.0", + "black >= 23.0.0, < 27.0.0", + "pylint >= 3.0.0, < 5.0.0", + "mypy >= 1.0.0, < 2.0.0", + "sphinx >= 7.0.0, < 10.0.0", + "sphinx_rtd_theme >= 2.0.0, < 4.0.0", + "sphinx-toolbox >= 3.0.0, < 5.0.0", + "myst_parser >= 2.0.0, < 6.0.0", + "opentelemetry-api >= 1.20.0, < 2.0.0", + "opentelemetry-sdk >= 1.20.0, < 2.0.0", +] diff --git a/setup.py b/setup.py deleted file mode 100644 index 5863ab5..0000000 --- a/setup.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python - -# ------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -------------------------------------------------------------------------- - -import re -import os.path -from io import open -from setuptools import find_packages, setup - -# Change the PACKAGE_NAME only to change folder and different name -PACKAGE_NAME = "featuremanagement" -PACKAGE_PPRINT_NAME = "Feature Management" - -# a-b-c => a/b/c -package_folder_path = PACKAGE_NAME.replace("-", "/") -# a-b-c => a.b.c -namespace_name = PACKAGE_NAME.replace("-", ".") - -# Version extraction inspired from 'requests' -with open(os.path.join(package_folder_path, "_version.py"), "r") as fd: - version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) - -if not version: - raise RuntimeError("Cannot find version information") - -with open("README.md", encoding="utf-8") as f: - readme = f.read() -with open("CHANGELOG.md", encoding="utf-8") as f: - changelog = f.read() - -setup( - name=PACKAGE_NAME, - version=version, - include_package_data=True, - description="Microsoft {} Library for Python".format(PACKAGE_PPRINT_NAME), - long_description=readme + "\n\n" + changelog, - long_description_content_type="text/markdown", - author="Microsoft Corporation", - author_email="appconfig@microsoft.com", - url="https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/appconfiguration/feature-management", - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Programming Language :: Python", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.14", - "License :: OSI Approved :: MIT License", - ], - zip_safe=False, - packages=find_packages(), - python_requires=">=3.10", - install_requires=[], - extras_require={ - "AzureMonitor": ["azure-monitor-events-extension<2.0.0"], - }, -) diff --git a/tests/requirements.txt b/tests/requirements.txt deleted file mode 100644 index f52da80..0000000 --- a/tests/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -azure-monitor-opentelemetry -azure-monitor-events-extension \ No newline at end of file From 81a52aea93545d19735ec442fcf7c51b50ed370d Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Mon, 9 Mar 2026 10:34:01 -0700 Subject: [PATCH 3/8] Create copilot-instructions.md --- .github/copilot-instructions.md | 56 +++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..e4fae2b --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,56 @@ +# Copilot Instructions + +## Python Environment + +Always activate and use the Python virtual environment when running Python commands: + +- On Windows: `.venv\Scripts\activate` +- On Linux/macOS: `source .venv/bin/activate` + +Use `python -m pip` instead of bare `pip` when installing packages. + +## Dev Setup + +Install all dependencies with: + +``` +python -m pip install -e ".[dev,test]" +``` + +## Python Version + +This project requires Python 3.10 or newer. + +## Project Structure + +- `featuremanagement/` — Synchronous feature management code +- `featuremanagement/aio/` — Async equivalents of feature management classes +- `featuremanagement/_models/` — Data models (feature flags, variants, telemetry) +- `featuremanagement/_time_window_filter/` — Time window filter with recurrence support +- `featuremanagement/azuremonitor/` — Optional Azure Monitor telemetry integration +- `tests/` — Unit tests (sync and async) +- `samples/` — Sample applications + +## Code Conventions + +- All source files must include the Microsoft copyright header. +- All modules must have a module-level docstring. +- Maximum line length is 120 characters. +- Use type annotations on all functions and methods. + +## Code Quality + +Run these before submitting changes: + +``` +black featuremanagement +pylint featuremanagement +mypy featuremanagement +pytest tests +``` + +## Testing + +- Sync tests are in `tests/test_*.py` +- Async tests use `pytest-asyncio` and are in files ending with `_async.py` +- Run tests with: `pytest tests` From ca8b856e511fd7ebee33d95a25448f6945b225bf Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Mon, 9 Mar 2026 10:41:24 -0700 Subject: [PATCH 4/8] formatting --- featuremanagement/__init__.py | 1 + featuremanagement/_defaultfilters.py | 1 + featuremanagement/_featurefilters.py | 1 + featuremanagement/_featuremanager.py | 1 + featuremanagement/_featuremanagerbase.py | 1 + featuremanagement/_models/__init__.py | 1 + featuremanagement/_models/_allocation.py | 1 + featuremanagement/_models/_evaluation_event.py | 1 + featuremanagement/_models/_feature_conditions.py | 1 + featuremanagement/_models/_feature_flag.py | 1 + featuremanagement/_models/_telemetry.py | 1 + featuremanagement/_models/_variant.py | 1 + featuremanagement/_models/_variant_assignment_reason.py | 1 + featuremanagement/_models/_variant_reference.py | 1 + featuremanagement/_time_window_filter/__init__.py | 1 + featuremanagement/_time_window_filter/_models.py | 1 + featuremanagement/_time_window_filter/_recurrence_evaluator.py | 1 + featuremanagement/_time_window_filter/_recurrence_validator.py | 1 + featuremanagement/aio/__init__.py | 1 + featuremanagement/aio/_defaultfilters.py | 1 + featuremanagement/aio/_featurefilters.py | 1 + featuremanagement/aio/_featuremanager.py | 1 + featuremanagement/azuremonitor/__init__.py | 1 + featuremanagement/azuremonitor/_send_telemetry.py | 1 + 24 files changed, 24 insertions(+) diff --git a/featuremanagement/__init__.py b/featuremanagement/__init__.py index adc0bee..2e0dbb2 100644 --- a/featuremanagement/__init__.py +++ b/featuremanagement/__init__.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Feature management library for Python.""" + from ._featuremanager import FeatureManager from ._featurefilters import FeatureFilter from ._defaultfilters import TimeWindowFilter, TargetingFilter diff --git a/featuremanagement/_defaultfilters.py b/featuremanagement/_defaultfilters.py index 4c529af..d36ddc6 100644 --- a/featuremanagement/_defaultfilters.py +++ b/featuremanagement/_defaultfilters.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Built-in feature filter implementations.""" + import logging import hashlib from datetime import datetime, timezone diff --git a/featuremanagement/_featurefilters.py b/featuremanagement/_featurefilters.py index ff685ea..c8b1b39 100644 --- a/featuremanagement/_featurefilters.py +++ b/featuremanagement/_featurefilters.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Base class for feature filters.""" + from abc import ABC, abstractmethod from typing import Mapping, Callable, Any, Optional diff --git a/featuremanagement/_featuremanager.py b/featuremanagement/_featuremanager.py index ca33c81..4d829a5 100644 --- a/featuremanagement/_featuremanager.py +++ b/featuremanagement/_featuremanager.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Synchronous feature manager implementation.""" + import logging from typing import cast, overload, Any, Optional, Dict, Mapping, List, Tuple from ._defaultfilters import TimeWindowFilter, TargetingFilter diff --git a/featuremanagement/_featuremanagerbase.py b/featuremanagement/_featuremanagerbase.py index 7dc109a..fac7f34 100644 --- a/featuremanagement/_featuremanagerbase.py +++ b/featuremanagement/_featuremanagerbase.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Base class for feature manager implementations.""" + import hashlib import logging from abc import ABC diff --git a/featuremanagement/_models/__init__.py b/featuremanagement/_models/__init__.py index e50022f..58f9403 100644 --- a/featuremanagement/_models/__init__.py +++ b/featuremanagement/_models/__init__.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Data models for feature management.""" + from ._feature_flag import FeatureFlag from ._variant import Variant from ._evaluation_event import EvaluationEvent diff --git a/featuremanagement/_models/_allocation.py b/featuremanagement/_models/_allocation.py index 6089040..dbdc4a4 100644 --- a/featuremanagement/_models/_allocation.py +++ b/featuremanagement/_models/_allocation.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Allocation model for feature variant assignment.""" + from typing import cast, List, Optional, Mapping, Dict, Any, Union from dataclasses import dataclass from ._constants import DEFAULT_WHEN_ENABLED, DEFAULT_WHEN_DISABLED, USER, GROUP, PERCENTILE, SEED diff --git a/featuremanagement/_models/_evaluation_event.py b/featuremanagement/_models/_evaluation_event.py index 23049d0..1b69b11 100644 --- a/featuremanagement/_models/_evaluation_event.py +++ b/featuremanagement/_models/_evaluation_event.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Evaluation event model for feature flag telemetry.""" + from dataclasses import dataclass from typing import Optional from ._feature_flag import FeatureFlag diff --git a/featuremanagement/_models/_feature_conditions.py b/featuremanagement/_models/_feature_conditions.py index 563b4ff..e60c5e6 100644 --- a/featuremanagement/_models/_feature_conditions.py +++ b/featuremanagement/_models/_feature_conditions.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Feature flag condition models.""" + from collections.abc import Mapping from typing import Any, Dict, List from ._constants import ( diff --git a/featuremanagement/_models/_feature_flag.py b/featuremanagement/_models/_feature_flag.py index d0db05a..0ce9eed 100644 --- a/featuremanagement/_models/_feature_flag.py +++ b/featuremanagement/_models/_feature_flag.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Feature flag model.""" + from typing import cast, List, Union, Optional, Mapping, Any from ._feature_conditions import FeatureConditions from ._allocation import Allocation diff --git a/featuremanagement/_models/_telemetry.py b/featuremanagement/_models/_telemetry.py index 2225f8c..c5387e7 100644 --- a/featuremanagement/_models/_telemetry.py +++ b/featuremanagement/_models/_telemetry.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Telemetry metadata model.""" + from typing import Dict from dataclasses import dataclass, field diff --git a/featuremanagement/_models/_variant.py b/featuremanagement/_models/_variant.py index 93458fb..da8695c 100644 --- a/featuremanagement/_models/_variant.py +++ b/featuremanagement/_models/_variant.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Variant model representing a feature flag variant.""" + from typing import Any diff --git a/featuremanagement/_models/_variant_assignment_reason.py b/featuremanagement/_models/_variant_assignment_reason.py index 06d2b0d..773c1cb 100644 --- a/featuremanagement/_models/_variant_assignment_reason.py +++ b/featuremanagement/_models/_variant_assignment_reason.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Enum for variant assignment reasons.""" + from enum import Enum diff --git a/featuremanagement/_models/_variant_reference.py b/featuremanagement/_models/_variant_reference.py index 28a1679..2c671fb 100644 --- a/featuremanagement/_models/_variant_reference.py +++ b/featuremanagement/_models/_variant_reference.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Variant reference model.""" + from dataclasses import dataclass from typing import Optional, Mapping, Any from ._constants import VARIANT_REFERENCE_NAME, CONFIGURATION_VALUE, STATUS_OVERRIDE diff --git a/featuremanagement/_time_window_filter/__init__.py b/featuremanagement/_time_window_filter/__init__.py index c955697..fb9263b 100644 --- a/featuremanagement/_time_window_filter/__init__.py +++ b/featuremanagement/_time_window_filter/__init__.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Time window filter with recurrence support.""" + from ._recurrence_evaluator import is_match from ._models import Recurrence, TimeWindowFilterSettings diff --git a/featuremanagement/_time_window_filter/_models.py b/featuremanagement/_time_window_filter/_models.py index 126e08a..b7ec442 100644 --- a/featuremanagement/_time_window_filter/_models.py +++ b/featuremanagement/_time_window_filter/_models.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Data models for the time window filter.""" + from enum import Enum from typing import Dict, Any, Optional, List from datetime import datetime diff --git a/featuremanagement/_time_window_filter/_recurrence_evaluator.py b/featuremanagement/_time_window_filter/_recurrence_evaluator.py index 4079ba5..7e7a8cb 100644 --- a/featuremanagement/_time_window_filter/_recurrence_evaluator.py +++ b/featuremanagement/_time_window_filter/_recurrence_evaluator.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Recurrence evaluation logic for the time window filter.""" + from datetime import datetime, timedelta from typing import Optional from ._models import RecurrencePatternType, RecurrenceRangeType, TimeWindowFilterSettings, OccurrenceInfo, Recurrence diff --git a/featuremanagement/_time_window_filter/_recurrence_validator.py b/featuremanagement/_time_window_filter/_recurrence_validator.py index 4f3cc06..160e026 100644 --- a/featuremanagement/_time_window_filter/_recurrence_validator.py +++ b/featuremanagement/_time_window_filter/_recurrence_validator.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Validation logic for recurrence settings.""" + from datetime import datetime, timedelta from typing import List from ._models import RecurrencePatternType, RecurrenceRangeType, Recurrence, RecurrencePattern, RecurrenceRange diff --git a/featuremanagement/aio/__init__.py b/featuremanagement/aio/__init__.py index 4c7a107..ff46abb 100644 --- a/featuremanagement/aio/__init__.py +++ b/featuremanagement/aio/__init__.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Async feature management support.""" + from ._featuremanager import FeatureManager from ._featurefilters import FeatureFilter from ._defaultfilters import TimeWindowFilter, TargetingFilter diff --git a/featuremanagement/aio/_defaultfilters.py b/featuremanagement/aio/_defaultfilters.py index 26572fc..e6b2d68 100644 --- a/featuremanagement/aio/_defaultfilters.py +++ b/featuremanagement/aio/_defaultfilters.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Built-in async feature filter implementations.""" + from typing import Mapping, Any from ._featurefilters import FeatureFilter from .._defaultfilters import ( diff --git a/featuremanagement/aio/_featurefilters.py b/featuremanagement/aio/_featurefilters.py index d53d5a3..627e1ed 100644 --- a/featuremanagement/aio/_featurefilters.py +++ b/featuremanagement/aio/_featurefilters.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Base class for async feature filters.""" + from abc import ABC, abstractmethod from typing import Mapping, Callable, Any, Optional diff --git a/featuremanagement/aio/_featuremanager.py b/featuremanagement/aio/_featuremanager.py index 6513652..4a7b475 100644 --- a/featuremanagement/aio/_featuremanager.py +++ b/featuremanagement/aio/_featuremanager.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Async feature manager implementation.""" + import inspect import logging from typing import cast, overload, Any, Optional, Dict, Mapping, List, Tuple diff --git a/featuremanagement/azuremonitor/__init__.py b/featuremanagement/azuremonitor/__init__.py index a2fdbae..b09ac6f 100644 --- a/featuremanagement/azuremonitor/__init__.py +++ b/featuremanagement/azuremonitor/__init__.py @@ -4,6 +4,7 @@ # license information. # ------------------------------------------------------------------------- """Azure Monitor telemetry integration for feature management.""" + from ._send_telemetry import publish_telemetry, track_event, TargetingSpanProcessor __all__ = [ diff --git a/featuremanagement/azuremonitor/_send_telemetry.py b/featuremanagement/azuremonitor/_send_telemetry.py index 55187b4..48c33d3 100644 --- a/featuremanagement/azuremonitor/_send_telemetry.py +++ b/featuremanagement/azuremonitor/_send_telemetry.py @@ -4,6 +4,7 @@ # license information. # -------------------------------------------------------------------------- """Telemetry publishing for feature evaluation events.""" + import logging import inspect from typing import Any, Callable, Dict, Optional From a19326a446edfe1564011e5c421e179791df4e23 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Mon, 9 Mar 2026 10:49:06 -0700 Subject: [PATCH 5/8] test and sample headers --- samples/feature_flag_sample.py | 1 + samples/feature_flag_with_azure_app_configuration_sample.py | 1 + samples/feature_variant_sample.py | 1 + samples/feature_variant_sample_with_targeting_accessor.py | 1 + samples/feature_variant_sample_with_telemetry.py | 2 ++ samples/quarty_sample.py | 1 + samples/random_filter.py | 2 ++ tests/test_default_feature_flags.py | 2 ++ tests/test_default_feature_flags_async.py | 2 ++ tests/test_feature_manager.py | 2 ++ tests/test_feature_manager_async.py | 2 ++ tests/test_feature_manager_refresh.py | 2 ++ tests/test_feature_variants.py | 2 ++ tests/test_feature_variants_async.py | 2 ++ tests/test_send_telemetry_appinsights.py | 2 ++ 15 files changed, 25 insertions(+) diff --git a/samples/feature_flag_sample.py b/samples/feature_flag_sample.py index f3aa59b..ad8c011 100644 --- a/samples/feature_flag_sample.py +++ b/samples/feature_flag_sample.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Sample demonstrating basic feature flag usage.""" import json import os diff --git a/samples/feature_flag_with_azure_app_configuration_sample.py b/samples/feature_flag_with_azure_app_configuration_sample.py index dedf3bd..13a3e35 100644 --- a/samples/feature_flag_with_azure_app_configuration_sample.py +++ b/samples/feature_flag_with_azure_app_configuration_sample.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Sample demonstrating feature flags with Azure App Configuration.""" from time import sleep import os diff --git a/samples/feature_variant_sample.py b/samples/feature_variant_sample.py index fb96308..1dac737 100644 --- a/samples/feature_variant_sample.py +++ b/samples/feature_variant_sample.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Sample demonstrating feature variants.""" import json import os diff --git a/samples/feature_variant_sample_with_targeting_accessor.py b/samples/feature_variant_sample_with_targeting_accessor.py index 887a01b..e416d27 100644 --- a/samples/feature_variant_sample_with_targeting_accessor.py +++ b/samples/feature_variant_sample_with_targeting_accessor.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Sample demonstrating feature variants with a targeting accessor.""" import json import os diff --git a/samples/feature_variant_sample_with_telemetry.py b/samples/feature_variant_sample_with_telemetry.py index 72991b8..915a034 100644 --- a/samples/feature_variant_sample_with_telemetry.py +++ b/samples/feature_variant_sample_with_telemetry.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Sample demonstrating feature variants with telemetry.""" + import json import os import sys diff --git a/samples/quarty_sample.py b/samples/quarty_sample.py index 8bfd08e..211e065 100644 --- a/samples/quarty_sample.py +++ b/samples/quarty_sample.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Sample demonstrating Quart web app with feature management.""" import uuid import os diff --git a/samples/random_filter.py b/samples/random_filter.py index 86084ae..1ee756f 100644 --- a/samples/random_filter.py +++ b/samples/random_filter.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- +"""Sample custom feature filter using random evaluation.""" + import random from featuremanagement import FeatureFilter diff --git a/tests/test_default_feature_flags.py b/tests/test_default_feature_flags.py index 7e4345a..b9c0dd1 100644 --- a/tests/test_default_feature_flags.py +++ b/tests/test_default_feature_flags.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Tests for built-in feature filters.""" + import unittest import pytest from featuremanagement import FeatureManager, TargetingContext diff --git a/tests/test_default_feature_flags_async.py b/tests/test_default_feature_flags_async.py index c8d2048..77eed66 100644 --- a/tests/test_default_feature_flags_async.py +++ b/tests/test_default_feature_flags_async.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Async tests for built-in feature filters.""" + from unittest import IsolatedAsyncioTestCase import pytest from featuremanagement.aio import FeatureManager diff --git a/tests/test_feature_manager.py b/tests/test_feature_manager.py index 7813835..b9a0fc6 100644 --- a/tests/test_feature_manager.py +++ b/tests/test_feature_manager.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Tests for the synchronous FeatureManager.""" + import unittest import pytest from featuremanagement import FeatureManager, FeatureFilter diff --git a/tests/test_feature_manager_async.py b/tests/test_feature_manager_async.py index 1f6c959..d224ba9 100644 --- a/tests/test_feature_manager_async.py +++ b/tests/test_feature_manager_async.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Tests for the async FeatureManager.""" + import unittest import pytest from featuremanagement.aio import FeatureManager, FeatureFilter diff --git a/tests/test_feature_manager_refresh.py b/tests/test_feature_manager_refresh.py index 39e23b2..148f2cb 100644 --- a/tests/test_feature_manager_refresh.py +++ b/tests/test_feature_manager_refresh.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Tests for feature manager configuration refresh.""" + import pytest from featuremanagement import FeatureManager from featuremanagement.aio import FeatureManager as AsyncFeatureManager diff --git a/tests/test_feature_variants.py b/tests/test_feature_variants.py index 860d70b..01e6959 100644 --- a/tests/test_feature_variants.py +++ b/tests/test_feature_variants.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Tests for feature variant assignment.""" + import unittest from featuremanagement import FeatureManager, FeatureFilter, TargetingContext diff --git a/tests/test_feature_variants_async.py b/tests/test_feature_variants_async.py index 9b4a71c..505a0e0 100644 --- a/tests/test_feature_variants_async.py +++ b/tests/test_feature_variants_async.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Async tests for feature variant assignment.""" + from unittest import IsolatedAsyncioTestCase from featuremanagement.aio import FeatureManager, FeatureFilter from featuremanagement import TargetingContext diff --git a/tests/test_send_telemetry_appinsights.py b/tests/test_send_telemetry_appinsights.py index 52eab4e..4922070 100644 --- a/tests/test_send_telemetry_appinsights.py +++ b/tests/test_send_telemetry_appinsights.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +"""Tests for Application Insights telemetry publishing.""" + import logging from unittest.mock import patch import pytest From eb121f0a22c0ae018fdb2f369553c547432a41fa Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Mon, 9 Mar 2026 10:50:24 -0700 Subject: [PATCH 6/8] pr comments --- pyproject.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f0fe2ca..d5de89a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ max-returns = 7 disable = ["duplicate-code"] [build-system] -requires = ["setuptools>=61.0"] +requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [project] @@ -50,6 +50,9 @@ version = {attr = "featuremanagement._version.VERSION"} [tool.setuptools.packages.find] include = ["featuremanagement*"] +[tool.setuptools.package-data] +featuremanagement = ["py.typed"] + [project.urls] Homepage = "https://github.com/microsoft/FeatureManagement-Python" Issues = "https://github.com/microsoft/FeatureManagement-Python/issues" From d719bbc21d286d9d50c724faf680055266291643 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Mon, 9 Mar 2026 11:07:51 -0700 Subject: [PATCH 7/8] Update copilot-instructions.md --- .github/copilot-instructions.md | 101 ++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 18 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index e4fae2b..d9cc428 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,27 +1,39 @@ -# Copilot Instructions +# FEATURE MANAGEMENT FOR PYTHON - COPILOT INSTRUCTIONS -## Python Environment +--- -Always activate and use the Python virtual environment when running Python commands: +## CORE PRINCIPLES -- On Windows: `.venv\Scripts\activate` -- On Linux/macOS: `source .venv/bin/activate` +### RULE 1: DO NOT REPEAT INSTRUCTIONS +**NEVER repeat instructions when guiding users. Users should follow instructions independently.** -Use `python -m pip` instead of bare `pip` when installing packages. +### RULE 2: REFERENCE OFFICIAL DOCUMENTATION +**ALWAYS** reference the [Azure SDK Python Design Guidelines](https://azure.github.io/azure-sdk/python_design.html) +- Link to specific pages when answering guidelines questions +- Use this as the authoritative source for SDK development guidance -## Dev Setup +### RULE 3: VERIFY ENVIRONMENT FIRST +**REQUIRED CONDITIONS:** +- Always activate the Python virtual environment before running Python commands: + - On Windows: `.venv\Scripts\activate` + - On Linux/macOS: `source .venv/bin/activate` +- Use `python -m pip` instead of bare `pip` when installing packages. + +--- + +## DEV SETUP Install all dependencies with: -``` +```bash python -m pip install -e ".[dev,test]" ``` -## Python Version +This project requires **Python 3.10 or newer**. -This project requires Python 3.10 or newer. +--- -## Project Structure +## PROJECT STRUCTURE - `featuremanagement/` — Synchronous feature management code - `featuremanagement/aio/` — Async equivalents of feature management classes @@ -31,25 +43,78 @@ This project requires Python 3.10 or newer. - `tests/` — Unit tests (sync and async) - `samples/` — Sample applications -## Code Conventions +--- + +## CODE CONVENTIONS - All source files must include the Microsoft copyright header. - All modules must have a module-level docstring. - Maximum line length is 120 characters. - Use type annotations on all functions and methods. -## Code Quality +--- -Run these before submitting changes: +## PYLINT OPERATIONS -``` -black featuremanagement +### RUNNING PYLINT + +**COMMAND:** +```bash pylint featuremanagement +``` + +### FIXING PYLINT WARNINGS + +**ALLOWED ACTIONS:** +- ✅ Fix warnings with 100% confidence +- ✅ Use existing files for all solutions +- ✅ Reference official guidelines + +**FORBIDDEN ACTIONS:** +- ❌ Fix warnings without complete confidence +- ❌ Create new files for solutions +- ❌ Import non-existent modules +- ❌ Add new dependencies/imports +- ❌ Make unnecessary large changes +- ❌ Change code style without reason +- ❌ Delete code without clear justification + +--- + +## MYPY OPERATIONS + +### RUNNING MYPY + +**COMMAND:** +```bash mypy featuremanagement -pytest tests ``` -## Testing +The project uses `strict = True` in `mypy.ini`. + +--- + +## CODE FORMATTING + +### RUNNING BLACK + +**COMMAND:** +```bash +black featuremanagement +``` + +Line length is configured to 120 in `pyproject.toml`. + +--- + +## TESTING + +### RUNNING TESTS + +**COMMAND:** +```bash +pytest tests +``` - Sync tests are in `tests/test_*.py` - Async tests use `pytest-asyncio` and are in files ending with `_async.py` From da7499cc97350f4a274eb2c928045d49269481e2 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Mon, 9 Mar 2026 12:08:44 -0700 Subject: [PATCH 8/8] pr comments --- .github/workflows/validate.yml | 6 +++--- pyproject.toml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index b7825f0..49d6d81 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -17,7 +17,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install ".[dev]" + python -m pip install ".[dev]" - name: Analysing the code with pylint run: | pylint featuremanagement @@ -29,9 +29,9 @@ jobs: uses: streetsidesoftware/cspell-action@v6.8.0 - name: Test with pytest run: | - pip install ".[test]" + python -m pip install ".[test]" pytest tests --doctest-modules --cov-report=xml --cov-report=html - name: Analysing the samples with pylint run: | - pip install -r samples/requirements.txt + python -m pip install -r samples/requirements.txt pylint --disable=missing-function-docstring,missing-class-docstring samples tests \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index d5de89a..da3aa6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ min-public-methods = 1 max-branches = 20 max-returns = 7 -[tool.pylint."messages control"] +[tool.pylint."messages_control"] disable = ["duplicate-code"] [build-system] @@ -60,8 +60,8 @@ Issues = "https://github.com/microsoft/FeatureManagement-Python/issues" [project.optional-dependencies] AzureMonitor = ["azure-monitor-events-extension<2.0.0"] test = [ - "azure-monitor-opentelemetry", - "azure-monitor-events-extension", + "azure-monitor-opentelemetry < 2.0.0", + "azure-monitor-events-extension < 2.0.0", ] dev = [ "pytest >= 7.0.0, < 10.0.0",