Skip to content

Commit fe81de1

Browse files
Add Python 3.9 support by using ParamSpec from typing_extensions and removing match statements
1 parent 9be28a4 commit fe81de1

File tree

13 files changed

+96
-84
lines changed

13 files changed

+96
-84
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.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.9+
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: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,20 @@ 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.9"
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 = "romain@coderamp.io"}]
1815
classifiers=[
1916
"Development Status :: 3 - Alpha",
2017
"Intended Audience :: Developers",
2118
"License :: OSI Approved :: MIT License",
19+
"Programming Language :: Python :: 3.9",
2220
"Programming Language :: Python :: 3.10",
2321
"Programming Language :: Python :: 3.11",
2422
"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.9",
2425
author="Romain Courtois",
2526
author_email="romain@coderamp.io",
2627
description="CLI tool to analyze and create text dumps of codebases for LLMs",

src/gitingest/cli.py

Lines changed: 11 additions & 10 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
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,
2425
exclude_pattern: tuple[str, ...],
2526
include_pattern: tuple[str, ...],
26-
branch: str | None,
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 : Optional[str]
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.
4243
exclude_pattern : tuple[str, ...]
43-
A tuple of patterns to exclude during the analysis. Files matching these patterns will be ignored.
44+
A tuple of patterns to exclude during the analysis. Files matching these patterns will be ignored.
4445
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 : Optional[str]
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,
5758
exclude_pattern: tuple[str, ...],
5859
include_pattern: tuple[str, ...],
59-
branch: str | None,
60+
branch: Optional[str],
6061
) -> None:
6162
"""
6263
Analyze a directory or repository and create a text dump of its contents.
@@ -68,7 +69,7 @@ async def _async_main(
6869
----------
6970
source : str
7071
The source directory or repository to analyze.
71-
output : str | None
72+
output : Optional[str]
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
@@ -77,7 +78,7 @@ async def _async_main(
7778
A tuple of patterns to exclude during the analysis. Files matching these patterns will be ignored.
7879
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 : Optional[str]
8182
The branch to clone (optional).
8283
8384
Raises

src/gitingest/notebook_utils.py

Lines changed: 10 additions & 12 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, Optional
88

99
from gitingest.exceptions import InvalidNotebookError
1010

@@ -63,7 +63,7 @@ def process_notebook(file: Path, include_output: bool = True) -> str:
6363
return "\n\n".join(result) + "\n"
6464

6565

66-
def _process_cell(cell: dict[str, Any], include_output: bool) -> str | None:
66+
def _process_cell(cell: dict[str, Any], include_output: bool) -> Optional[str]:
6767
"""
6868
Process a Jupyter notebook cell and return the cell content as a string.
6969
@@ -76,7 +76,7 @@ def _process_cell(cell: dict[str, Any], include_output: bool) -> str | None:
7676
7777
Returns
7878
-------
79-
str | None
79+
Optional[str]
8080
The cell content as a string, or None if the cell is empty.
8181
8282
Raises
@@ -139,15 +139,13 @@ def _extract_output(output: dict[str, Any]) -> list[str]:
139139
"""
140140
output_type = output["output_type"]
141141

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

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

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

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

src/gitingest/query_ingestion.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import platform
66
from fnmatch import fnmatch
77
from pathlib import Path
8-
from typing import Any
8+
from typing import Any, Optional, Union
99

1010
import tiktoken
1111

@@ -42,7 +42,7 @@ def _normalize_path(path: Path) -> Path:
4242
return Path(os.path.normpath(str(path)))
4343

4444

45-
def _normalize_path_str(path: str | Path) -> str:
45+
def _normalize_path_str(path: Union[Path, str]) -> str:
4646
"""
4747
Convert path to string with forward slashes for consistent output.
4848
@@ -293,10 +293,10 @@ def _sort_children(children: list[dict[str, Any]]) -> list[dict[str, Any]]:
293293
def _scan_directory(
294294
path: Path,
295295
query: ParsedQuery,
296-
seen_paths: set[Path] | None = None,
296+
seen_paths: Optional[set[Path]] = None,
297297
depth: int = 0,
298-
stats: dict[str, int] | None = None,
299-
) -> dict[str, Any] | None:
298+
stats: Optional[dict[str, int]] = None,
299+
) -> Optional[dict[str, Any]]:
300300
"""
301301
Recursively analyze a directory and its contents with safety limits.
302302
@@ -573,7 +573,7 @@ def _process_item(
573573
def _extract_files_content(
574574
query: ParsedQuery,
575575
node: dict[str, Any],
576-
files: list[dict[str, Any]] | None = None,
576+
files: Optional[list[dict[str, Any]]] = None,
577577
) -> list[dict[str, Any]]:
578578
"""
579579
Recursively collect all text files with their contents.
@@ -733,7 +733,7 @@ def _create_tree_structure(query: ParsedQuery, node: dict[str, Any], prefix: str
733733
return tree
734734

735735

736-
def _generate_token_string(context_string: str) -> str | None:
736+
def _generate_token_string(context_string: str) -> Optional[str]:
737737
"""
738738
Return the number of tokens in a text string.
739739
@@ -747,7 +747,7 @@ def _generate_token_string(context_string: str) -> str | None:
747747
748748
Returns
749749
-------
750-
str | None
750+
Optional[str]
751751
The formatted number of tokens as a string (e.g., '1.2k', '1.2M'), or `None` if an error occurs.
752752
"""
753753
try:

src/gitingest/query_parser.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import warnings
88
from dataclasses import dataclass
99
from pathlib import Path
10+
from typing import Optional, Union
1011
from urllib.parse import unquote, urlparse
1112

1213
from gitingest.config import MAX_FILE_SIZE, TMP_BASE_PATH
@@ -32,28 +33,28 @@ class ParsedQuery: # pylint: disable=too-many-instance-attributes
3233
Dataclass to store the parsed details of the repository or file path.
3334
"""
3435

35-
user_name: str | None
36-
repo_name: str | None
36+
user_name: Optional[str]
37+
repo_name: Optional[str]
3738
subpath: str
3839
local_path: Path
39-
url: str | None
40+
url: Optional[str]
4041
slug: str
4142
id: str
42-
type: str | None = None
43-
branch: str | None = None
44-
commit: str | None = None
43+
type: Optional[str] = None
44+
branch: Optional[str] = None
45+
commit: Optional[str] = None
4546
max_file_size: int = MAX_FILE_SIZE
46-
ignore_patterns: set[str] | None = None
47-
include_patterns: set[str] | None = None
48-
pattern_type: str | None = None
47+
ignore_patterns: Optional[set[str]] = None
48+
include_patterns: Optional[set[str]] = None
49+
pattern_type: Optional[str] = None
4950

5051

5152
async def parse_query(
5253
source: str,
5354
max_file_size: int,
5455
from_web: bool,
55-
include_patterns: set[str] | str | None = None,
56-
ignore_patterns: set[str] | str | None = None,
56+
include_patterns: Optional[Union[str, set[str]]] = None,
57+
ignore_patterns: Optional[Union[str, set[str]]] = None,
5758
) -> ParsedQuery:
5859
"""
5960
Parse the input source (URL or path) to extract relevant details for the query.
@@ -70,9 +71,9 @@ async def parse_query(
7071
The maximum file size in bytes to include.
7172
from_web : bool
7273
Flag indicating whether the source is a web URL.
73-
include_patterns : set[str] | str | None, optional
74+
include_patterns : Union[str, set[str]], optional
7475
Patterns to include, by default None. Can be a set of strings or a single string.
75-
ignore_patterns : set[str] | str | None, optional
76+
ignore_patterns : Union[str, set[str]], optional
7677
Patterns to ignore, by default None. Can be a set of strings or a single string.
7778
7879
Returns
@@ -208,7 +209,7 @@ async def _parse_repo_source(source: str) -> ParsedQuery:
208209
return parsed
209210

210211

211-
async def _configure_branch_and_subpath(remaining_parts: list[str], url: str) -> str | None:
212+
async def _configure_branch_and_subpath(remaining_parts: list[str], url: str) -> Optional[str]:
212213
"""
213214
Configure the branch and subpath based on the remaining parts of the URL.
214215
Parameters
@@ -219,7 +220,7 @@ async def _configure_branch_and_subpath(remaining_parts: list[str], url: str) ->
219220
The URL of the repository.
220221
Returns
221222
-------
222-
str | None
223+
str, optional
223224
The branch name if found, otherwise None.
224225
225226
"""
@@ -283,7 +284,7 @@ def _normalize_pattern(pattern: str) -> str:
283284
return pattern
284285

285286

286-
def _parse_patterns(pattern: set[str] | str) -> set[str]:
287+
def _parse_patterns(pattern: Union[str, set[str]]) -> set[str]:
287288
"""
288289
Parse and validate file/directory patterns for inclusion or exclusion.
289290

0 commit comments

Comments
 (0)