Skip to content

Commit 86d0b76

Browse files
committed
chore: modernize Python tooling setup (Phase 1)
- Migrate to comprehensive pyproject.toml configuration - Move all package metadata from setup.py - Configure build system with setuptools backend - Include private package in distribution - Use SPDX license identifier (Apache-2.0) - Add uv for fast dependency management with .python-version - Configure ruff for linting and formatting (replacing pylint/yapf) - Use modern Python formatting standards (Black-style) - Enable Python 3.10+ syntax improvements - Configure to match existing code style where possible - Update GitHub Actions workflows - Replace venv/pip with uv for faster installs - Replace pylint with ruff check - Replace yapf with ruff format - Use python -m build instead of setup.py - Add .git-blame-ignore-revs for future formatting changes - Update .gitignore to exclude uv.lock This is Phase 1 of the modernization plan. Next steps: - Remove setup.py after verifying builds work - Apply ruff formatting in separate commit - Add tox configuration for multi-version testing
1 parent 436f4c7 commit 86d0b76

File tree

6 files changed

+227
-49
lines changed

6 files changed

+227
-49
lines changed

.git-blame-ignore-revs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# .git-blame-ignore-revs
2+
# Use this file to ignore commits in git blame that are just formatting changes
3+
# Configure with: git config blame.ignoreRevsFile .git-blame-ignore-revs
4+
5+
# Switch from yapf to ruff formatting (one-time reformatting)
6+
# TODO: Add commit hash here after formatting commit

.github/workflows/ci.yaml

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,20 @@ jobs:
1616

1717
steps:
1818
- uses: actions/checkout@v3
19+
- name: Install uv
20+
uses: astral-sh/setup-uv@v2
21+
with:
22+
version: "latest"
1923
- name: Set up Python ${{ matrix.python }}
2024
uses: actions/setup-python@v4
2125
with:
2226
python-version: ${{ matrix.python }}
2327
- name: Install dependencies
2428
run: |
25-
python${{ matrix.python }} -m venv venv
26-
source venv/bin/activate
27-
pip3 install --upgrade pip
28-
python${{ matrix.python }} -m pip install -e ".[dev]"
29+
uv sync --extra dev
2930
- name: Test with pytest & coverage
3031
run: |
31-
source venv/bin/activate
32-
python${{ matrix.python }} -m pytest --cov=src --cov-report term --cov-report html --cov-report xml -vv
32+
uv run pytest --cov=src --cov-report term --cov-report html --cov-report xml -vv
3333
# TODO requires activation for this repository on codecov website first.
3434
# - name: Upload coverage to Codecov
3535
# uses: codecov/codecov-action@v3
@@ -38,44 +38,43 @@ jobs:
3838
runs-on: ubuntu-latest
3939
steps:
4040
- uses: actions/checkout@v3
41+
- name: Install uv
42+
uses: astral-sh/setup-uv@v2
43+
with:
44+
version: "latest"
4145
- name: Set up Python
4246
uses: actions/setup-python@v4
4347
with:
4448
python-version: "3.10"
4549
- name: Install dependencies
4650
run: |
47-
python3.10 -m venv venv
48-
source venv/bin/activate
49-
pip3 install --upgrade pip
50-
python3.10 -m pip install -e ".[dev]"
51-
- name: Lint with pylint
51+
uv sync --extra dev
52+
- name: Lint with ruff
5253
run: |
53-
source venv/bin/activate
54-
python3.10 -m pylint $(git ls-files '*.py')
55-
- name: Lint with mypy
54+
uv run ruff check .
55+
- name: Type check with mypy
5656
run: |
57-
source venv/bin/activate
58-
python3.10 -m mypy .
57+
uv run mypy .
5958
6059
docs:
6160
runs-on: ubuntu-latest
6261
steps:
6362
- uses: actions/checkout@v3
63+
- name: Install uv
64+
uses: astral-sh/setup-uv@v2
65+
with:
66+
version: "latest"
6467
- name: Set up Python
6568
uses: actions/setup-python@v4
6669
with:
6770
python-version: "3.10"
6871
- name: Install dependencies
6972
run: |
70-
python3.10 -m venv venv
71-
source venv/bin/activate
72-
pip3 install --upgrade pip
73-
python3.10 -m pip install -e ".[dev]"
73+
uv sync --extra dev
7474
- name: Generate Reference Docs
7575
run: |
76-
source venv/bin/activate
7776
mkdir ./docs/build
78-
./docs/generate.sh --out=./docs/build/ --pypath=src/
77+
uv run ./docs/generate.sh --out=./docs/build/ --pypath=src/
7978
- uses: actions/upload-artifact@v4
8079
name: Upload Docs Preview
8180
with:
@@ -86,17 +85,17 @@ jobs:
8685
runs-on: ubuntu-latest
8786
steps:
8887
- uses: actions/checkout@v3
88+
- name: Install uv
89+
uses: astral-sh/setup-uv@v2
90+
with:
91+
version: "latest"
8992
- name: Set up Python
9093
uses: actions/setup-python@v4
9194
with:
9295
python-version: "3.10"
9396
- name: Install dependencies
9497
run: |
95-
python3.10 -m venv venv
96-
source venv/bin/activate
97-
pip3 install --upgrade pip
98-
python3.10 -m pip install -e ".[dev]"
99-
- name: Check Formatting
98+
uv sync --extra dev
99+
- name: Check Formatting with ruff
100100
run: |
101-
source venv/bin/activate
102-
yapf -d -r -p .
101+
uv run ruff format --check .

.github/workflows/release.yaml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,25 +36,28 @@ jobs:
3636
- name: Checkout source for staging
3737
uses: actions/checkout@v3
3838

39+
- name: Install uv
40+
uses: astral-sh/setup-uv@v2
41+
with:
42+
version: "latest"
43+
3944
- name: Set up Python
4045
uses: actions/setup-python@v4
4146
with:
4247
python-version: '3.10'
4348

4449
- name: Install dependencies
4550
run: |
46-
pip install --upgrade pip
47-
python -m pip install -e ".[dev]"
51+
uv sync --extra dev
4852
4953
- name: Test with pytest & coverage
5054
run: |
51-
python -m pytest --cov=src --cov-report term --cov-report html --cov-report xml -vv
55+
uv run pytest --cov=src --cov-report term --cov-report html --cov-report xml -vv
5256
5357
# Build the Python Wheel and the source distribution.
5458
- name: Package release artifacts
5559
run: |
56-
python -m pip install setuptools wheel
57-
python setup.py bdist_wheel sdist
60+
uv run python -m build
5861
5962
# Attach the packaged artifacts to the workflow output. These can be manually
6063
# downloaded for later inspection if necessary.

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ profile_default/
8282
ipython_config.py
8383

8484
# pyenv
85-
.python-version
85+
# .python-version # We're now tracking this for uv
8686

8787
# pipenv
8888
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
@@ -139,3 +139,6 @@ doc/dist
139139
.idea
140140
.vscode/*
141141
!.vscode/settings.json
142+
143+
# uv
144+
uv.lock

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.10

pyproject.toml

Lines changed: 180 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,189 @@
1-
[tool.pytest.ini_options]
2-
pythonpath = [
3-
".", "src/",
1+
[build-system]
2+
requires = ["setuptools>=61.0", "wheel"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "firebase_functions"
7+
description = "Firebase Functions Python SDK"
8+
readme = "README.md"
9+
license = "Apache-2.0"
10+
authors = [{name = "Firebase Team"}]
11+
keywords = ["firebase", "functions", "google", "cloud"]
12+
classifiers = [
13+
"Development Status :: 4 - Beta",
14+
"Intended Audience :: Developers",
15+
"Topic :: Software Development :: Build Tools",
16+
"Programming Language :: Python :: 3.10",
17+
"Programming Language :: Python :: 3.11",
18+
"Programming Language :: Python :: 3.12",
19+
]
20+
requires-python = ">=3.10"
21+
dynamic = ["version"]
22+
dependencies = [
23+
"flask>=2.1.2",
24+
"functions-framework>=3.0.0",
25+
"firebase-admin>=6.0.0",
26+
"pyyaml>=6.0",
27+
"typing-extensions>=4.4.0",
28+
"cloudevents>=1.2.0,<2.0.0",
29+
"flask-cors>=3.0.10",
30+
"pyjwt[crypto]>=2.5.0",
31+
"google-events==0.5.0",
32+
"google-cloud-firestore>=2.11.0",
33+
]
34+
35+
[project.optional-dependencies]
36+
dev = [
37+
"pytest>=7.1.2",
38+
"pytest-cov>=3.0.0",
39+
"mypy>=1.0.0",
40+
"sphinx>=6.1.3",
41+
"sphinxcontrib-napoleon>=0.7",
42+
"toml>=0.10.2",
43+
"google-cloud-tasks>=2.13.1",
44+
"ruff>=0.1.0",
45+
"pre-commit>=3.0.0",
46+
"tox>=4.0.0",
47+
"build>=1.0.0",
48+
]
49+
test = [
50+
"pytest>=7.1.2",
51+
"pytest-cov>=3.0.0",
52+
"google-cloud-tasks>=2.13.1",
53+
]
54+
docs = [
55+
"sphinx>=6.1.3",
56+
"sphinxcontrib-napoleon>=0.7",
457
]
58+
59+
[project.urls]
60+
Homepage = "https://github.com/firebase/firebase-functions-python"
61+
Repository = "https://github.com/firebase/firebase-functions-python"
62+
Issues = "https://github.com/firebase/firebase-functions-python/issues"
63+
64+
[tool.setuptools]
65+
packages = ["firebase_functions", "firebase_functions.private"]
66+
package-dir = {"" = "src"}
67+
include-package-data = true
68+
69+
[tool.setuptools.package-data]
70+
firebase_functions = ["py.typed"]
71+
72+
[tool.setuptools.dynamic]
73+
version = {attr = "firebase_functions.__version__"}
74+
75+
[tool.pytest.ini_options]
76+
pythonpath = [".", "src/"]
77+
testpaths = ["tests"]
78+
addopts = "-v --cov=firebase_functions --cov-report=term-missing"
79+
580
[tool.coverage]
681
[tool.coverage.run]
82+
source = ["src/firebase_functions"]
783
omit = [
8-
'__init__.py',
9-
'tests/*',
10-
'*/tests/*',
84+
"__init__.py",
85+
"tests/*",
86+
"*/tests/*",
1187
]
1288

1389
[tool.coverage.report]
1490
skip_empty = true
15-
[tool.yapf]
16-
based_on_style = "google"
17-
indent_width = 4
18-
[tool.yapfignore]
19-
ignore_patterns = [
20-
"venv",
21-
"build",
22-
"dist",
91+
show_missing = true
92+
precision = 2
93+
94+
[tool.mypy]
95+
python_version = "3.10"
96+
exclude = ["build", "dist", "venv", "docs"]
97+
ignore_missing_imports = true
98+
enable_incomplete_feature = ["Unpack"]
99+
strict = true
100+
warn_return_any = true
101+
warn_unused_configs = true
102+
disallow_untyped_defs = true
103+
disallow_incomplete_defs = true
104+
check_untyped_defs = true
105+
no_implicit_optional = true
106+
warn_redundant_casts = true
107+
warn_unused_ignores = true
108+
warn_no_return = true
109+
warn_unreachable = true
110+
strict_equality = true
111+
112+
[[tool.mypy.overrides]]
113+
module = "yaml.*"
114+
ignore_missing_imports = true
115+
116+
[[tool.mypy.overrides]]
117+
module = "cloudevents.*"
118+
ignore_missing_imports = true
119+
120+
[tool.ruff]
121+
target-version = "py310"
122+
line-length = 100
123+
indent-width = 4
124+
125+
[tool.ruff.lint]
126+
# Enable Pyflakes `E` and `F` codes by default, plus additional rules
127+
select = [
128+
"E", # pycodestyle errors
129+
"W", # pycodestyle warnings
130+
"F", # pyflakes
131+
"I", # isort
132+
"UP", # pyupgrade
133+
"B", # flake8-bugbear
134+
"C4", # flake8-comprehensions
135+
"PL", # pylint
136+
"PIE", # flake8-pie
137+
"RET", # flake8-return
138+
"SIM", # flake8-simplify
139+
"TCH", # flake8-type-checking
140+
"PTH", # flake8-use-pathlib
141+
"ERA", # eradicate
142+
"TRY", # tryceratops
143+
"RUF", # Ruff-specific rules
23144
]
145+
146+
# Disable specific rules to match existing pylint configuration
147+
ignore = [
148+
"PLR0913", # Too many arguments
149+
"PLR0912", # Too many branches
150+
"PLR0915", # Too many statements
151+
"PLR2004", # Magic value used in comparison
152+
"PLW0603", # Using the global statement
153+
"PLC0415", # Import outside toplevel
154+
"E501", # Line too long (handled by formatter)
155+
"TRY003", # Avoid specifying long messages outside the exception class
156+
"TRY400", # Use logging.exception instead of logging.error
157+
"B008", # Do not perform function calls in argument defaults
158+
"SIM108", # Use ternary operator
159+
"SIM114", # Combine if branches using logical or
160+
"RET504", # Unnecessary variable assignment before return
161+
"RET505", # Unnecessary else after return
162+
"RET506", # Unnecessary else after raise
163+
# Removed UP007 to allow modern Union type syntax
164+
]
165+
166+
# Enable Python 3.10+ specific rules
167+
pyupgrade.keep-runtime-typing = false
168+
169+
[tool.ruff.lint.pylint]
170+
max-args = 10
171+
max-branches = 20
172+
max-returns = 10
173+
max-statements = 100
174+
175+
[tool.ruff.lint.per-file-ignores]
176+
"tests/*" = ["PLR2004", "PLR0913"]
177+
"setup.py" = ["ALL"]
178+
179+
[tool.ruff.lint.isort]
180+
known-first-party = ["firebase_functions"]
181+
combine-as-imports = true
182+
split-on-trailing-comma = false
183+
184+
[tool.ruff.format]
185+
quote-style = "double" # Consistent double quotes (Black-style)
186+
indent-style = "space"
187+
skip-magic-trailing-comma = false # Always add trailing commas for cleaner diffs
188+
line-ending = "auto"
189+
docstring-code-format = true # Format code examples in docstrings

0 commit comments

Comments
 (0)