codefetch-cli 1.2.7__tar.gz → 1.2.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codefetch_cli-1.2.7/codefetch_cli.egg-info → codefetch_cli-1.2.8}/PKG-INFO +1 -1
- {codefetch_cli-1.2.7 → codefetch_cli-1.2.8}/codefetch/__init__.py +160 -160
- {codefetch_cli-1.2.7 → codefetch_cli-1.2.8}/codefetch/cli.py +1004 -983
- {codefetch_cli-1.2.7 → codefetch_cli-1.2.8/codefetch_cli.egg-info}/PKG-INFO +1 -1
- {codefetch_cli-1.2.7 → codefetch_cli-1.2.8}/pyproject.toml +28 -28
- {codefetch_cli-1.2.7 → codefetch_cli-1.2.8}/setup.py +30 -30
- {codefetch_cli-1.2.7 → codefetch_cli-1.2.8}/LICENSE +0 -0
- {codefetch_cli-1.2.7 → codefetch_cli-1.2.8}/README.md +0 -0
- {codefetch_cli-1.2.7 → codefetch_cli-1.2.8}/codefetch_cli.egg-info/SOURCES.txt +0 -0
- {codefetch_cli-1.2.7 → codefetch_cli-1.2.8}/codefetch_cli.egg-info/dependency_links.txt +0 -0
- {codefetch_cli-1.2.7 → codefetch_cli-1.2.8}/codefetch_cli.egg-info/entry_points.txt +0 -0
- {codefetch_cli-1.2.7 → codefetch_cli-1.2.8}/codefetch_cli.egg-info/top_level.txt +0 -0
- {codefetch_cli-1.2.7 → codefetch_cli-1.2.8}/setup.cfg +0 -0
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
"""
|
|
2
|
-
CodeFetch - Easily preview and download code files from GitHub repositories.
|
|
3
|
-
|
|
4
|
-
Usage in Python:
|
|
5
|
-
import codefetch as cf
|
|
6
|
-
|
|
7
|
-
# 1. Fetch file content as string:
|
|
8
|
-
content = cf.get("torvalds/linux", "Makefile")
|
|
9
|
-
# or shorthand target string:
|
|
10
|
-
content = cf.get("torvalds/linux/Makefile")
|
|
11
|
-
|
|
12
|
-
# 2. Download file to local disk:
|
|
13
|
-
cf.download("torvalds/linux", "Makefile", output="linux_makefile.txt")
|
|
14
|
-
|
|
15
|
-
# 3. List available code files:
|
|
16
|
-
files = cf.list("torvalds/linux")
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
__version__ = "1.2.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def get(target, filename=None, branch=None):
|
|
23
|
-
"""
|
|
24
|
-
Fetch and return the text content of a file from GitHub.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
target: 'owner/repo' or full path 'owner/repo/path/to/file' or full GitHub URL.
|
|
28
|
-
filename: Optional path to file in repo if target is 'owner/repo'.
|
|
29
|
-
branch: Branch name (default: None, automatically detects repo default branch).
|
|
30
|
-
|
|
31
|
-
Returns:
|
|
32
|
-
str: File content.
|
|
33
|
-
|
|
34
|
-
Raises:
|
|
35
|
-
FileNotFoundError: If the file does not exist in the repository.
|
|
36
|
-
ValueError: If arguments cannot be resolved into a repository and filename.
|
|
37
|
-
"""
|
|
38
|
-
import urllib.request
|
|
39
|
-
from .cli import (
|
|
40
|
-
get_default_branch,
|
|
41
|
-
check_branch_exists,
|
|
42
|
-
fetch_raw_single,
|
|
43
|
-
get_repo_tree,
|
|
44
|
-
resolve_file_in_tree,
|
|
45
|
-
parse_repo_identifier,
|
|
46
|
-
resolve_invocation,
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
if filename:
|
|
50
|
-
owner, repo = parse_repo_identifier(target)
|
|
51
|
-
file_path = filename.strip("/\\")
|
|
52
|
-
else:
|
|
53
|
-
owner, repo, resolved_branch, file_path = resolve_invocation(
|
|
54
|
-
[target], None, None, branch, None, None
|
|
55
|
-
)
|
|
56
|
-
if resolved_branch:
|
|
57
|
-
branch = resolved_branch
|
|
58
|
-
|
|
59
|
-
if not owner or not repo or not file_path:
|
|
60
|
-
raise ValueError(f"Could not resolve owner, repo, and filename from target='{target}', filename='{filename}'")
|
|
61
|
-
|
|
62
|
-
target_branch = branch
|
|
63
|
-
if not target_branch:
|
|
64
|
-
target_branch = get_default_branch(owner, repo)
|
|
65
|
-
elif not check_branch_exists(owner, repo, target_branch):
|
|
66
|
-
target_branch = get_default_branch(owner, repo)
|
|
67
|
-
|
|
68
|
-
# 1. Try fast direct raw fetch
|
|
69
|
-
content = fetch_raw_single(owner, repo, target_branch, file_path)
|
|
70
|
-
if content is not None:
|
|
71
|
-
return content
|
|
72
|
-
|
|
73
|
-
# 2. Try repository tree search
|
|
74
|
-
tree_data = get_repo_tree(owner, repo, target_branch)
|
|
75
|
-
matched = resolve_file_in_tree(tree_data, file_path)
|
|
76
|
-
if matched:
|
|
77
|
-
url = f"https://raw.githubusercontent.com/{owner}/{repo}/{target_branch}/{matched}"
|
|
78
|
-
req = urllib.request.Request(url, headers={"User-Agent": "codefetch"})
|
|
79
|
-
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
80
|
-
return resp.read().decode("utf-8")
|
|
81
|
-
|
|
82
|
-
raise FileNotFoundError(f"File '{file_path}' not found in {owner}/{repo} ({target_branch})")
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def download(target, filename=None, output=None, branch=None):
|
|
86
|
-
"""
|
|
87
|
-
Download a file from GitHub and save it locally.
|
|
88
|
-
|
|
89
|
-
Args:
|
|
90
|
-
target: 'owner/repo' or full path 'owner/repo/path/to/file'.
|
|
91
|
-
filename: Optional path to file in repo if target is 'owner/repo'.
|
|
92
|
-
output: Destination filename/path on local disk (default: original filename).
|
|
93
|
-
branch: Branch name (default: None, automatically detects repo default branch).
|
|
94
|
-
|
|
95
|
-
Returns:
|
|
96
|
-
str: Path to the downloaded file.
|
|
97
|
-
"""
|
|
98
|
-
content = get(target, filename=filename, branch=branch)
|
|
99
|
-
dest_name = output
|
|
100
|
-
if not dest_name:
|
|
101
|
-
resolved_file = filename if filename else target.rsplit("/", 1)[-1]
|
|
102
|
-
dest_name = resolved_file.rsplit("/", 1)[-1]
|
|
103
|
-
|
|
104
|
-
with open(dest_name, "w", encoding="utf-8") as f:
|
|
105
|
-
f.write(content)
|
|
106
|
-
|
|
107
|
-
return dest_name
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def list(target, branch=None):
|
|
111
|
-
"""
|
|
112
|
-
List supported code files in a repository.
|
|
113
|
-
|
|
114
|
-
Args:
|
|
115
|
-
target: 'owner/repo' or repository URL.
|
|
116
|
-
branch: Branch name (default: None, automatically detects repo default branch).
|
|
117
|
-
|
|
118
|
-
Returns:
|
|
119
|
-
list[str]: Relative paths of code files in the repository.
|
|
120
|
-
"""
|
|
121
|
-
from .cli import (
|
|
122
|
-
parse_repo_identifier,
|
|
123
|
-
get_default_branch,
|
|
124
|
-
check_branch_exists,
|
|
125
|
-
get_repo_tree,
|
|
126
|
-
CODE_EXTENSIONS,
|
|
127
|
-
NAMED_FILES,
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
owner, repo = parse_repo_identifier(target)
|
|
131
|
-
if not owner or not repo:
|
|
132
|
-
raise ValueError(f"Invalid repository identifier: '{target}'")
|
|
133
|
-
|
|
134
|
-
target_branch = branch
|
|
135
|
-
if not target_branch or not check_branch_exists(owner, repo, target_branch):
|
|
136
|
-
target_branch = get_default_branch(owner, repo)
|
|
137
|
-
|
|
138
|
-
tree_data = get_repo_tree(owner, repo, target_branch)
|
|
139
|
-
files = []
|
|
140
|
-
for item in tree_data.get("tree", []):
|
|
141
|
-
if item.get("type") != "blob":
|
|
142
|
-
continue
|
|
143
|
-
path = item.get("path", "")
|
|
144
|
-
base_name = path.rsplit("/", 1)[-1].lower()
|
|
145
|
-
suffix = ""
|
|
146
|
-
if "." in base_name:
|
|
147
|
-
suffix = "." + base_name.rsplit(".", 1)[-1]
|
|
148
|
-
if suffix in CODE_EXTENSIONS or base_name in NAMED_FILES:
|
|
149
|
-
files.append(path)
|
|
150
|
-
|
|
151
|
-
files.sort(key=str.lower)
|
|
152
|
-
return files
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
__all__ = [
|
|
156
|
-
"__version__",
|
|
157
|
-
"get",
|
|
158
|
-
"download",
|
|
159
|
-
"list",
|
|
160
|
-
]
|
|
1
|
+
"""
|
|
2
|
+
CodeFetch - Easily preview and download code files from GitHub repositories.
|
|
3
|
+
|
|
4
|
+
Usage in Python:
|
|
5
|
+
import codefetch as cf
|
|
6
|
+
|
|
7
|
+
# 1. Fetch file content as string:
|
|
8
|
+
content = cf.get("torvalds/linux", "Makefile")
|
|
9
|
+
# or shorthand target string:
|
|
10
|
+
content = cf.get("torvalds/linux/Makefile")
|
|
11
|
+
|
|
12
|
+
# 2. Download file to local disk:
|
|
13
|
+
cf.download("torvalds/linux", "Makefile", output="linux_makefile.txt")
|
|
14
|
+
|
|
15
|
+
# 3. List available code files:
|
|
16
|
+
files = cf.list("torvalds/linux")
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
__version__ = "1.2.8"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get(target, filename=None, branch=None):
|
|
23
|
+
"""
|
|
24
|
+
Fetch and return the text content of a file from GitHub.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
target: 'owner/repo' or full path 'owner/repo/path/to/file' or full GitHub URL.
|
|
28
|
+
filename: Optional path to file in repo if target is 'owner/repo'.
|
|
29
|
+
branch: Branch name (default: None, automatically detects repo default branch).
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
str: File content.
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
FileNotFoundError: If the file does not exist in the repository.
|
|
36
|
+
ValueError: If arguments cannot be resolved into a repository and filename.
|
|
37
|
+
"""
|
|
38
|
+
import urllib.request
|
|
39
|
+
from .cli import (
|
|
40
|
+
get_default_branch,
|
|
41
|
+
check_branch_exists,
|
|
42
|
+
fetch_raw_single,
|
|
43
|
+
get_repo_tree,
|
|
44
|
+
resolve_file_in_tree,
|
|
45
|
+
parse_repo_identifier,
|
|
46
|
+
resolve_invocation,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if filename:
|
|
50
|
+
owner, repo = parse_repo_identifier(target)
|
|
51
|
+
file_path = filename.strip("/\\")
|
|
52
|
+
else:
|
|
53
|
+
owner, repo, resolved_branch, file_path = resolve_invocation(
|
|
54
|
+
[target], None, None, branch, None, None
|
|
55
|
+
)
|
|
56
|
+
if resolved_branch:
|
|
57
|
+
branch = resolved_branch
|
|
58
|
+
|
|
59
|
+
if not owner or not repo or not file_path:
|
|
60
|
+
raise ValueError(f"Could not resolve owner, repo, and filename from target='{target}', filename='{filename}'")
|
|
61
|
+
|
|
62
|
+
target_branch = branch
|
|
63
|
+
if not target_branch:
|
|
64
|
+
target_branch = get_default_branch(owner, repo)
|
|
65
|
+
elif not check_branch_exists(owner, repo, target_branch):
|
|
66
|
+
target_branch = get_default_branch(owner, repo)
|
|
67
|
+
|
|
68
|
+
# 1. Try fast direct raw fetch
|
|
69
|
+
content = fetch_raw_single(owner, repo, target_branch, file_path)
|
|
70
|
+
if content is not None:
|
|
71
|
+
return content
|
|
72
|
+
|
|
73
|
+
# 2. Try repository tree search
|
|
74
|
+
tree_data = get_repo_tree(owner, repo, target_branch)
|
|
75
|
+
matched = resolve_file_in_tree(tree_data, file_path)
|
|
76
|
+
if matched:
|
|
77
|
+
url = f"https://raw.githubusercontent.com/{owner}/{repo}/{target_branch}/{matched}"
|
|
78
|
+
req = urllib.request.Request(url, headers={"User-Agent": "codefetch"})
|
|
79
|
+
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
80
|
+
return resp.read().decode("utf-8")
|
|
81
|
+
|
|
82
|
+
raise FileNotFoundError(f"File '{file_path}' not found in {owner}/{repo} ({target_branch})")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def download(target, filename=None, output=None, branch=None):
|
|
86
|
+
"""
|
|
87
|
+
Download a file from GitHub and save it locally.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
target: 'owner/repo' or full path 'owner/repo/path/to/file'.
|
|
91
|
+
filename: Optional path to file in repo if target is 'owner/repo'.
|
|
92
|
+
output: Destination filename/path on local disk (default: original filename).
|
|
93
|
+
branch: Branch name (default: None, automatically detects repo default branch).
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
str: Path to the downloaded file.
|
|
97
|
+
"""
|
|
98
|
+
content = get(target, filename=filename, branch=branch)
|
|
99
|
+
dest_name = output
|
|
100
|
+
if not dest_name:
|
|
101
|
+
resolved_file = filename if filename else target.rsplit("/", 1)[-1]
|
|
102
|
+
dest_name = resolved_file.rsplit("/", 1)[-1]
|
|
103
|
+
|
|
104
|
+
with open(dest_name, "w", encoding="utf-8") as f:
|
|
105
|
+
f.write(content)
|
|
106
|
+
|
|
107
|
+
return dest_name
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def list(target, branch=None):
|
|
111
|
+
"""
|
|
112
|
+
List supported code files in a repository.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
target: 'owner/repo' or repository URL.
|
|
116
|
+
branch: Branch name (default: None, automatically detects repo default branch).
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
list[str]: Relative paths of code files in the repository.
|
|
120
|
+
"""
|
|
121
|
+
from .cli import (
|
|
122
|
+
parse_repo_identifier,
|
|
123
|
+
get_default_branch,
|
|
124
|
+
check_branch_exists,
|
|
125
|
+
get_repo_tree,
|
|
126
|
+
CODE_EXTENSIONS,
|
|
127
|
+
NAMED_FILES,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
owner, repo = parse_repo_identifier(target)
|
|
131
|
+
if not owner or not repo:
|
|
132
|
+
raise ValueError(f"Invalid repository identifier: '{target}'")
|
|
133
|
+
|
|
134
|
+
target_branch = branch
|
|
135
|
+
if not target_branch or not check_branch_exists(owner, repo, target_branch):
|
|
136
|
+
target_branch = get_default_branch(owner, repo)
|
|
137
|
+
|
|
138
|
+
tree_data = get_repo_tree(owner, repo, target_branch)
|
|
139
|
+
files = []
|
|
140
|
+
for item in tree_data.get("tree", []):
|
|
141
|
+
if item.get("type") != "blob":
|
|
142
|
+
continue
|
|
143
|
+
path = item.get("path", "")
|
|
144
|
+
base_name = path.rsplit("/", 1)[-1].lower()
|
|
145
|
+
suffix = ""
|
|
146
|
+
if "." in base_name:
|
|
147
|
+
suffix = "." + base_name.rsplit(".", 1)[-1]
|
|
148
|
+
if suffix in CODE_EXTENSIONS or base_name in NAMED_FILES:
|
|
149
|
+
files.append(path)
|
|
150
|
+
|
|
151
|
+
files.sort(key=str.lower)
|
|
152
|
+
return files
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
__all__ = [
|
|
156
|
+
"__version__",
|
|
157
|
+
"get",
|
|
158
|
+
"download",
|
|
159
|
+
"list",
|
|
160
|
+
]
|