Skip to content

Commit 4565425

Browse files
feat: Add Python 3.7 Support and Restore Compatibility with Older Syntax (#181)
* Add Python 3.9 support by using ParamSpec from typing_extensions and removing match statements * Add Python 3.7 support by reverting inline generics and removing walrus usage * Update pyproject.toml Signed-off-by: Filip Christiansen <[email protected]>
1 parent acfe68b commit 4565425

20 files changed

+210
-196
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
fail-fast: true
1414
matrix:
1515
os: [ubuntu-latest, macos-latest, windows-latest]
16-
python-version: ["3.10", "3.11", "3.12", "3.13"]
16+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
1717

1818
steps:
1919
- uses: actions/checkout@v4

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ repos:
4848
hooks:
4949
- id: pyupgrade
5050
description: "Automatically upgrade syntax for newer versions."
51-
args: [--py3-plus, --py36-plus, --py38-plus, --py39-plus, --py310-plus]
51+
args: [--py3-plus, --py36-plus]
5252

5353
- repo: https://github.com/pre-commit/pygrep-hooks
5454
rev: v1.10.0

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ You can also replace `hub` with `ingest` in any GitHub URL to access the corespo
2626
- **CLI tool**: Run it as a shell command
2727
- **Python package**: Import it in your code
2828

29+
## 📚 Requirements
30+
31+
- Python 3.7+
32+
2933
## 📦 Installation
3034

3135
``` bash
@@ -61,7 +65,7 @@ gitingest --help
6165

6266
This will write the digest in a text file (default `digest.txt`) in your current working directory.
6367

64-
## 🐛 Python package usage
68+
## 🐍 Python package usage
6569

6670
```python
6771
# Synchronous usage
@@ -81,7 +85,7 @@ result = asyncio.run(ingest_async("path/to/directory"))
8185

8286
By default, this won't write a file but can be enabled with the `output` argument.
8387

84-
## 🌐 Self-host
88+
## 🐳 Self-host
8589

8690
1. Build the image:
8791

@@ -104,7 +108,7 @@ If you are hosting it on a domain, you can specify the allowed hostnames via env
104108
ALLOWED_HOSTS="example.com, localhost, 127.0.0.1"
105109
```
106110

107-
## ✔️ Contributing to Gitingest
111+
## 🤝 Contributing
108112

109113
### Non-technical ways to contribute
110114

@@ -128,6 +132,6 @@ Gitingest aims to be friendly for first time contributors, with a simple python
128132

129133
Check out the NPM alternative 📦 Repomix: <https://github.com/yamadashy/repomix>
130134

131-
## Project Growth
135+
## 🚀 Project Growth
132136

133137
[![Star History Chart](https://api.star-history.com/svg?repos=cyclotruc/gitingest&type=Date)](https://star-history.com/#cyclotruc/gitingest&Date)

pyproject.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@ name = "gitingest"
33
version = "0.1.3"
44
description="CLI tool to analyze and create text dumps of codebases for LLMs"
55
readme = {file = "README.md", content-type = "text/markdown" }
6-
requires-python = ">= 3.10"
6+
requires-python = ">= 3.8"
77
dependencies = [
88
"click>=8.0.0",
9-
"fastapi[standard]",
10-
"python-dotenv",
11-
"slowapi",
12-
"starlette",
139
"tiktoken",
14-
"uvicorn",
10+
"typing_extensions; python_version < '3.10'",
1511
]
12+
1613
license = {file = "LICENSE"}
1714
authors = [{name = "Romain Courtois", email = "[email protected]"}]
1815
classifiers=[
1916
"Development Status :: 3 - Alpha",
2017
"Intended Audience :: Developers",
2118
"License :: OSI Approved :: MIT License",
19+
"Programming Language :: Python :: 3.7",
20+
"Programming Language :: Python :: 3.8",
21+
"Programming Language :: Python :: 3.9",
2222
"Programming Language :: Python :: 3.10",
2323
"Programming Language :: Python :: 3.11",
2424
"Programming Language :: Python :: 3.12",

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
install_requires=[
1515
"click>=8.0.0",
1616
"tiktoken",
17+
"typing_extensions; python_version < '3.10'",
1718
],
1819
entry_points={
1920
"console_scripts": [
2021
"gitingest=gitingest.cli:main",
2122
],
2223
},
23-
python_requires=">=3.6",
24+
python_requires=">=3.7",
2425
author="Romain Courtois",
2526
author_email="[email protected]",
2627
description="CLI tool to analyze and create text dumps of codebases for LLMs",

src/gitingest/cli.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# pylint: disable=no-value-for-parameter
44

55
import asyncio
6+
from typing import Optional, Tuple
67

78
import click
89

@@ -19,31 +20,31 @@
1920
@click.option("--branch", "-b", default=None, help="Branch to clone and ingest")
2021
def main(
2122
source: str,
22-
output: str | None,
23+
output: Optional[str],
2324
max_size: int,
24-
exclude_pattern: tuple[str, ...],
25-
include_pattern: tuple[str, ...],
26-
branch: str | None,
25+
exclude_pattern: Tuple[str, ...],
26+
include_pattern: Tuple[str, ...],
27+
branch: Optional[str],
2728
):
2829
"""
29-
Main entry point for the CLI. This function is called when the CLI is run as a script.
30+
Main entry point for the CLI. This function is called when the CLI is run as a script.
3031
3132
It calls the async main function to run the command.
3233
3334
Parameters
3435
----------
3536
source : str
3637
The source directory or repository to analyze.
37-
output : str | None
38+
output : str, optional
3839
The path where the output file will be written. If not specified, the output will be written
3940
to a file named `<repo_name>.txt` in the current directory.
4041
max_size : int
4142
The maximum file size to process, in bytes. Files larger than this size will be ignored.
42-
exclude_pattern : tuple[str, ...]
43+
exclude_pattern : Tuple[str, ...]
4344
A tuple of patterns to exclude during the analysis. Files matching these patterns will be ignored.
44-
include_pattern : tuple[str, ...]
45+
include_pattern : Tuple[str, ...]
4546
A tuple of patterns to include during the analysis. Only files matching these patterns will be processed.
46-
branch : str | None
47+
branch : str, optional
4748
The branch to clone (optional).
4849
"""
4950
# Main entry point for the CLI. This function is called when the CLI is run as a script.
@@ -52,11 +53,11 @@ def main(
5253

5354
async def _async_main(
5455
source: str,
55-
output: str | None,
56+
output: Optional[str],
5657
max_size: int,
57-
exclude_pattern: tuple[str, ...],
58-
include_pattern: tuple[str, ...],
59-
branch: str | None,
58+
exclude_pattern: Tuple[str, ...],
59+
include_pattern: Tuple[str, ...],
60+
branch: Optional[str],
6061
) -> None:
6162
"""
6263
Analyze a directory or repository and create a text dump of its contents.
@@ -68,16 +69,16 @@ async def _async_main(
6869
----------
6970
source : str
7071
The source directory or repository to analyze.
71-
output : str | None
72+
output : str, optional
7273
The path where the output file will be written. If not specified, the output will be written
7374
to a file named `<repo_name>.txt` in the current directory.
7475
max_size : int
7576
The maximum file size to process, in bytes. Files larger than this size will be ignored.
76-
exclude_pattern : tuple[str, ...]
77+
exclude_pattern : Tuple[str, ...]
7778
A tuple of patterns to exclude during the analysis. Files matching these patterns will be ignored.
78-
include_pattern : tuple[str, ...]
79+
include_pattern : Tuple[str, ...]
7980
A tuple of patterns to include during the analysis. Only files matching these patterns will be processed.
80-
branch : str | None
81+
branch : str, optional
8182
The branch to clone (optional).
8283
8384
Raises

src/gitingest/ignore_patterns.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
""" Default ignore patterns for Gitingest. """
22

3-
DEFAULT_IGNORE_PATTERNS: set[str] = {
3+
from typing import Set
4+
5+
DEFAULT_IGNORE_PATTERNS: Set[str] = {
46
# Python
57
"*.pyc",
68
"*.pyo",

src/gitingest/notebook_utils.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import warnings
55
from itertools import chain
66
from pathlib import Path
7-
from typing import Any
7+
from typing import Any, Dict, List, Optional
88

99
from gitingest.exceptions import InvalidNotebookError
1010

@@ -32,12 +32,13 @@ def process_notebook(file: Path, include_output: bool = True) -> str:
3232
"""
3333
try:
3434
with file.open(encoding="utf-8") as f:
35-
notebook: dict[str, Any] = json.load(f)
35+
notebook: Dict[str, Any] = json.load(f)
3636
except json.JSONDecodeError as e:
3737
raise InvalidNotebookError(f"Invalid JSON in notebook: {file}") from e
3838

3939
# Check if the notebook contains worksheets
40-
if worksheets := notebook.get("worksheets"):
40+
worksheets = notebook.get("worksheets")
41+
if worksheets:
4142
warnings.warn(
4243
"Worksheets are deprecated as of IPEP-17. Consider updating the notebook. "
4344
"(See: https://github.com/jupyter/nbformat and "
@@ -57,26 +58,27 @@ def process_notebook(file: Path, include_output: bool = True) -> str:
5758
result = ["# Jupyter notebook converted to Python script."]
5859

5960
for cell in cells:
60-
if cell_str := _process_cell(cell, include_output=include_output):
61+
cell_str = _process_cell(cell, include_output=include_output)
62+
if cell_str:
6163
result.append(cell_str)
6264

6365
return "\n\n".join(result) + "\n"
6466

6567

66-
def _process_cell(cell: dict[str, Any], include_output: bool) -> str | None:
68+
def _process_cell(cell: Dict[str, Any], include_output: bool) -> Optional[str]:
6769
"""
6870
Process a Jupyter notebook cell and return the cell content as a string.
6971
7072
Parameters
7173
----------
72-
cell : dict[str, Any]
74+
cell : Dict[str, Any]
7375
The cell dictionary from a Jupyter notebook.
7476
include_output : bool
7577
Whether to include cell outputs in the generated script
7678
7779
Returns
7880
-------
79-
str | None
81+
str, optional
8082
The cell content as a string, or None if the cell is empty.
8183
8284
Raises
@@ -101,7 +103,8 @@ def _process_cell(cell: dict[str, Any], include_output: bool) -> str | None:
101103
return f'"""\n{cell_str}\n"""'
102104

103105
# Add cell output as comments
104-
if include_output and (outputs := cell.get("outputs")):
106+
outputs = cell.get("outputs")
107+
if include_output and outputs:
105108

106109
# Include cell outputs as comments
107110
output_lines = []
@@ -118,18 +121,18 @@ def _process_cell(cell: dict[str, Any], include_output: bool) -> str | None:
118121
return cell_str
119122

120123

121-
def _extract_output(output: dict[str, Any]) -> list[str]:
124+
def _extract_output(output: Dict[str, Any]) -> List[str]:
122125
"""
123126
Extract the output from a Jupyter notebook cell.
124127
125128
Parameters
126129
----------
127-
output : dict[str, Any]
130+
output : Dict[str, Any]
128131
The output dictionary from a Jupyter notebook cell.
129132
130133
Returns
131134
-------
132-
list[str]
135+
List[str]
133136
The output as a list of strings.
134137
135138
Raises
@@ -139,15 +142,13 @@ def _extract_output(output: dict[str, Any]) -> list[str]:
139142
"""
140143
output_type = output["output_type"]
141144

142-
match output_type:
143-
case "stream":
144-
return output["text"]
145+
if output_type == "stream":
146+
return output["text"]
145147

146-
case "execute_result" | "display_data":
147-
return output["data"]["text/plain"]
148+
if output_type in ("execute_result", "display_data"):
149+
return output["data"]["text/plain"]
148150

149-
case "error":
150-
return [f"Error: {output['ename']}: {output['evalue']}"]
151+
if output_type == "error":
152+
return [f"Error: {output['ename']}: {output['evalue']}"]
151153

152-
case _:
153-
raise ValueError(f"Unknown output type: {output_type}")
154+
raise ValueError(f"Unknown output type: {output_type}")

0 commit comments

Comments
 (0)