codefetch-cli 1.2.7__py3-none-any.whl → 1.2.8__py3-none-any.whl

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/__init__.py CHANGED
@@ -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.7"
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
+ ]