panopticas 0.0.9__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Peter Freiberg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.2
2
+ Name: panopticas
3
+ Version: 0.0.9
4
+ Summary: The panopticas CLI tool.
5
+ Author-email: Peter Freiberg <peter.freiberg@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 Peter Freiberg
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://panopticas.io
29
+ Project-URL: Issues, https://github.com/kospex/panopticas/issues
30
+ Project-URL: Repository, https://github.com/kospex/panopticas.git
31
+ Keywords: analytics,git
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Programming Language :: Python
35
+ Requires-Python: >=3.11
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ Requires-Dist: Click
39
+ Requires-Dist: prettytable
40
+ Requires-Dist: pathspec
41
+
42
+ # panopticas
43
+
44
+ Discover insights into the types of data and functions used in your code.
45
+
46
+ Inspired by tools like [enry](https://github.com/go-enry/go-enry) and [linguist](https://github.com/github-linguist/linguist)
47
+
48
+ Initial feaures: language detector and metadata identifiers, based on the filename extension, filename details and the shebang line.
49
+
50
+ The official documentation can be found at [panopticas.io](https://panopticas.io)
51
+
52
+ ## Installation
53
+
54
+ > pip install panopticas
55
+
56
+ ## Usage
57
+
58
+ Change into the directory you want check the file types of and then run
59
+
60
+ > panopticas assess
61
+
62
+ To check a single file and get some metadata
63
+
64
+ > panopticas file FILENAME
65
+
66
+ ## Development
67
+
68
+ If you want to check out the [panopitcas repo](https://github.com/kospex/panopticas) and work on bug fixes, use the pip "editable" install to set up the _panopticas_ CLI for your shell using:
69
+ > pip install -e .
70
+
71
+
72
+
@@ -0,0 +1,31 @@
1
+ # panopticas
2
+
3
+ Discover insights into the types of data and functions used in your code.
4
+
5
+ Inspired by tools like [enry](https://github.com/go-enry/go-enry) and [linguist](https://github.com/github-linguist/linguist)
6
+
7
+ Initial feaures: language detector and metadata identifiers, based on the filename extension, filename details and the shebang line.
8
+
9
+ The official documentation can be found at [panopticas.io](https://panopticas.io)
10
+
11
+ ## Installation
12
+
13
+ > pip install panopticas
14
+
15
+ ## Usage
16
+
17
+ Change into the directory you want check the file types of and then run
18
+
19
+ > panopticas assess
20
+
21
+ To check a single file and get some metadata
22
+
23
+ > panopticas file FILENAME
24
+
25
+ ## Development
26
+
27
+ If you want to check out the [panopitcas repo](https://github.com/kospex/panopticas) and work on bug fixes, use the pip "editable" install to set up the _panopticas_ CLI for your shell using:
28
+ > pip install -e .
29
+
30
+
31
+
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=72.1.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "panopticas"
7
+ version = "0.0.9"
8
+ description = "The panopticas CLI tool."
9
+ authors = [{ name = "Peter Freiberg", email = "peter.freiberg@gmail.com" }]
10
+ dependencies = ["Click", "prettytable", "pathspec"]
11
+ requires-python = ">=3.11"
12
+ readme = "README.md"
13
+ keywords = ["analytics", "git"]
14
+ license = { file = "LICENSE" }
15
+
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python",
20
+ ]
21
+
22
+ [project.scripts]
23
+ panopticas = "panopticas_cli:cli"
24
+
25
+ [project.urls]
26
+ Homepage = "https://panopticas.io"
27
+ Issues = "https://github.com/kospex/panopticas/issues"
28
+ Repository = "https://github.com/kospex/panopticas.git"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.2
2
+ Name: panopticas
3
+ Version: 0.0.9
4
+ Summary: The panopticas CLI tool.
5
+ Author-email: Peter Freiberg <peter.freiberg@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 Peter Freiberg
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://panopticas.io
29
+ Project-URL: Issues, https://github.com/kospex/panopticas/issues
30
+ Project-URL: Repository, https://github.com/kospex/panopticas.git
31
+ Keywords: analytics,git
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Programming Language :: Python
35
+ Requires-Python: >=3.11
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ Requires-Dist: Click
39
+ Requires-Dist: prettytable
40
+ Requires-Dist: pathspec
41
+
42
+ # panopticas
43
+
44
+ Discover insights into the types of data and functions used in your code.
45
+
46
+ Inspired by tools like [enry](https://github.com/go-enry/go-enry) and [linguist](https://github.com/github-linguist/linguist)
47
+
48
+ Initial feaures: language detector and metadata identifiers, based on the filename extension, filename details and the shebang line.
49
+
50
+ The official documentation can be found at [panopticas.io](https://panopticas.io)
51
+
52
+ ## Installation
53
+
54
+ > pip install panopticas
55
+
56
+ ## Usage
57
+
58
+ Change into the directory you want check the file types of and then run
59
+
60
+ > panopticas assess
61
+
62
+ To check a single file and get some metadata
63
+
64
+ > panopticas file FILENAME
65
+
66
+ ## Development
67
+
68
+ If you want to check out the [panopitcas repo](https://github.com/kospex/panopticas) and work on bug fixes, use the pip "editable" install to set up the _panopticas_ CLI for your shell using:
69
+ > pip install -e .
70
+
71
+
72
+
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/panopticas.py
5
+ src/panopticas_cli.py
6
+ src/panopticas.egg-info/PKG-INFO
7
+ src/panopticas.egg-info/SOURCES.txt
8
+ src/panopticas.egg-info/dependency_links.txt
9
+ src/panopticas.egg-info/entry_points.txt
10
+ src/panopticas.egg-info/requires.txt
11
+ src/panopticas.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ panopticas = panopticas_cli:cli
@@ -0,0 +1,3 @@
1
+ Click
2
+ prettytable
3
+ pathspec
@@ -0,0 +1,2 @@
1
+ panopticas
2
+ panopticas_cli
@@ -0,0 +1,436 @@
1
+ """
2
+ Analysis functions for Panopticas.
3
+ """
4
+ import os
5
+ import re
6
+ import pathspec
7
+
8
+ VERSION = "0.0.8"
9
+
10
+ EXT_FILETYPES = {
11
+ '.c': 'C',
12
+ '.cpp': 'C++',
13
+ '.cs': 'C#',
14
+ '.css': 'CSS',
15
+ '.csv': 'CSV',
16
+ '.dockerignore': 'Dockerignore',
17
+ '.gitignore': 'Gitignore',
18
+ '.gitattributes': 'GitAttributes',
19
+ '.go': 'Go',
20
+ '.gif': "GIF",
21
+ '.h': 'C Header',
22
+ '.htm': 'HTML',
23
+ '.html': 'HTML',
24
+ '.ico': 'ICO',
25
+ '.ini': 'INI',
26
+ '.ipynb': 'Jupyter Notebook',
27
+ '.java': 'Java',
28
+ '.jpg': 'JPEG',
29
+ '.jpeg': 'JPEG',
30
+ '.js': 'JavaScript',
31
+ '.json': 'JSON',
32
+ '.jsx': 'JSX',
33
+ '.kt': 'Kotlin',
34
+ '.m': 'Objective-C',
35
+ '.mailmap': 'Mailmap',
36
+ '.md': 'Markdown',
37
+ '.nvmrc': "nvmrc",
38
+ '.php': 'PHP',
39
+ '.pl': 'Perl',
40
+ '.pm': 'Perl',
41
+ '.png': 'PNG',
42
+ '.py': 'Python',
43
+ '.r': 'R',
44
+ '.rb': 'Ruby',
45
+ '.rs': 'Rust',
46
+ '.rst': 'ReStructuredText',
47
+ '.scala': 'Scala',
48
+ '.sh': 'Shell',
49
+ '.sql': 'SQL',
50
+ '.sqlfluff': 'SQLFluff',
51
+ '.sqlfluffignore': 'SQLFluffIgnore',
52
+ '.svg': 'SVG',
53
+ '.swift': 'Swift',
54
+ '.tf': 'Terraform',
55
+ '.toml': 'TOML',
56
+ '.ts': 'TypeScript',
57
+ '.tsv': 'TSV',
58
+ '.tsx': 'TSX',
59
+ '.txt': 'Text',
60
+ '.vue': 'Vue',
61
+ '.xml': 'XML',
62
+ '.yaml': 'YAML',
63
+ '.yml': 'YAML',
64
+ # Special cases for files without extensions or .format files
65
+ "codeowners": "CODEOWNERS",
66
+ 'dockerfile': 'Dockerfile',
67
+ 'makefile': 'Makefile',
68
+ }
69
+
70
+ LANGUAGE_BY_BASENAME = {
71
+ 'go.mod': 'go.mod',
72
+ 'go.sum': 'go.sum',
73
+ }
74
+
75
+
76
+ def get_fileext(file_path):
77
+ """ Get the file extension of a file """
78
+ file_type = None
79
+
80
+ #if os.path.isfile(file_path):
81
+ # file_type = os.path.splitext(file_path)[1]
82
+ file_type = os.path.splitext(file_path)[1]
83
+
84
+ if file_type:
85
+ return file_type
86
+ else:
87
+ return os.path.basename(file_path)
88
+
89
+ def get_extension_filetype(file_ext):
90
+ """ Get the file extension of a file, using an exact match """
91
+
92
+ if file_ext:
93
+ return EXT_FILETYPES.get(file_ext.lower(), None)
94
+ else:
95
+ return None
96
+
97
+ def get_filename_metatypes(file_path):
98
+ """
99
+ Return an array of metatypes based on the file_path
100
+ For example:
101
+ pyproject.toml will return build, dependencies
102
+ .github/workflows/python-app.yml will return Github, workflow
103
+ """
104
+
105
+ filename = os.path.basename(file_path).lower()
106
+ ext = get_fileext(file_path)
107
+
108
+ file_no_ext = os.path.splitext(filename)[0]
109
+
110
+ tags = []
111
+
112
+ if ext == ".pm":
113
+ # Perl module
114
+ # Languauge should already be set by the extension
115
+ tags.append("module")
116
+
117
+ if filename == "pyproject.toml":
118
+ tags.append("build")
119
+ tags.append("dependencies")
120
+ tags.append("Python")
121
+
122
+ if ".github" in file_path:
123
+ tags.append("Github")
124
+ tags.append("Git")
125
+
126
+ if filename == '.gitattributes':
127
+ tags.append("Git")
128
+
129
+ if filename == "eslint.config.js":
130
+ tags.extend(["JavaScript", "linter", "eslint", "config"])
131
+
132
+ if filename == ".mailmap":
133
+ tags.append("Git")
134
+
135
+ if is_pip_requirements(filename):
136
+ #if filename == "requirements.txt":
137
+ tags.append("pip")
138
+ tags.append("Python")
139
+ tags.append("dependencies")
140
+
141
+ if filename == '.sqlfluff':
142
+ tags.append("SQLFluff")
143
+ tags.append("SQL")
144
+ tags.append("linter")
145
+
146
+ if filename == ".mailmap":
147
+ tags.append("Git")
148
+
149
+ if filename == '.nvmrc':
150
+ tags.append("Node")
151
+ tags.append("dependencies")
152
+
153
+ # Usually the filename will be uppercase CODEOWNERS for GitHub
154
+ if filename == "codeowners":
155
+ tags.append("Git")
156
+
157
+ if filename == ".gitignore":
158
+ tags.append("Git")
159
+ tags.append("ignore")
160
+
161
+ if filename == "dockerfile":
162
+ tags.append("IaC")
163
+ tags.append("Docker")
164
+ tags.append("dependencies")
165
+
166
+ if filename == ".dockerignore":
167
+ tags.append("Docker")
168
+ tags.append("ignore")
169
+
170
+ if filename == "makefile":
171
+ tags.append("build")
172
+
173
+ if ".github/workflows" in file_path:
174
+ tags.append("workflow")
175
+
176
+ if filename == "go.mod":
177
+ tags.append("Go")
178
+ tags.append("module")
179
+ tags.append("dependencies")
180
+
181
+ if filename == "go.sum":
182
+ tags.append("Go")
183
+ tags.append("dependencies")
184
+ # TODO - check what we actually want to call hashes etc
185
+ # maybe integrity-checks
186
+ tags.append("checksum")
187
+
188
+ if filename == ".sqlfluffignore":
189
+ tags.append("SQLFluff")
190
+ tags.append("ignore")
191
+
192
+ if filename == "codefresh.yml":
193
+ tags.append("pipeline")
194
+ tags.append("Codefresh")
195
+
196
+ if filename == ".travis.yml":
197
+ tags.append("pipeline")
198
+ tags.append("TravisCI")
199
+
200
+ if filename in ( "package.json", "package-lock.json"):
201
+ tags.append("npm")
202
+ tags.append("dependencies")
203
+
204
+ if filename == "pom.xml":
205
+ tags.append("maven")
206
+ tags.append("build")
207
+ tags.append("dependencies")
208
+
209
+ if filename == "build.gradle":
210
+ tags.append("gradle")
211
+ tags.append("build")
212
+ tags.append("dependencies")
213
+
214
+ if file_no_ext == "license":
215
+ tags.append("license")
216
+
217
+ return tags
218
+
219
+ def check_shebang(file_path):
220
+ """ Check if a file has a shebang """
221
+ try:
222
+
223
+ with open(file_path) as file:
224
+ first_line = file.readline()
225
+ if first_line and first_line.startswith("#!"):
226
+ return first_line.strip()
227
+ else:
228
+ return None
229
+
230
+ except FileNotFoundError:
231
+ # TODO - better logging instead of print
232
+ #print(f"File {file_path} not found")
233
+ return None
234
+ except UnicodeDecodeError:
235
+ # TODO - log this exception
236
+ return None
237
+
238
+
239
+ def get_shebang_language(shebang):
240
+ """ Return the language of a shebang """
241
+ lang = extract_shebang_language(shebang)
242
+ return lang
243
+
244
+ def load_gitignore_patterns(directory):
245
+ """
246
+ Load gitignore patterns from a directory
247
+ """
248
+ gitignore_path = os.path.join(directory, '.gitignore')
249
+
250
+ if os.path.exists(gitignore_path):
251
+
252
+ with open(gitignore_path, 'r', encoding='utf-8') as file:
253
+ patterns = file.read().splitlines()
254
+ # Add .git to the patterns
255
+ patterns.append('.git')
256
+ # These seem to be ignore by git
257
+ patterns.append('.jekyll-cache')
258
+ patterns.append('.ruff_cache')
259
+ patterns.append('.DS_Store')
260
+
261
+ return pathspec.PathSpec.from_lines('gitwildmatch', patterns)
262
+ return None
263
+
264
+ def identify_files(directory):
265
+ """
266
+ Identify files in a directory.
267
+ Returns a dict of the relative path filenams to their file_type
268
+ """
269
+
270
+ gitignore_spec = load_gitignore_patterns(directory)
271
+
272
+ file_paths = {}
273
+
274
+ for root, _, files in os.walk(directory):
275
+ for file in files:
276
+ full_path = os.path.join(root, file)
277
+
278
+ relative_path = os.path.relpath(full_path, directory)
279
+ if gitignore_spec and gitignore_spec.match_file(relative_path):
280
+ continue
281
+
282
+ ftype = get_language(full_path)
283
+
284
+ if directory == ".":
285
+ full_path = full_path.removeprefix("./")
286
+
287
+ # TODO - see if we want to add a parameter to return the full path
288
+ #file_paths[fulle_path] = ftype
289
+ file_paths[relative_path] = ftype
290
+
291
+ return file_paths
292
+
293
+ def find_files(directory,all_files=None):
294
+ """
295
+ Find all files in a directory, honoring the gitignore patterns.
296
+ If all_files = True, then find everything.
297
+ Returns a list of the relative path filenames
298
+ """
299
+
300
+ gitignore_spec = load_gitignore_patterns(directory)
301
+ if all_files:
302
+ gitignore_spec = None
303
+
304
+ file_paths = []
305
+
306
+ for root, _, files in os.walk(directory):
307
+ for file in files:
308
+ full_path = os.path.join(root, file)
309
+
310
+ relative_path = os.path.relpath(full_path, directory)
311
+ if gitignore_spec and gitignore_spec.match_file(relative_path):
312
+ # Ignore and continue
313
+ continue
314
+
315
+ if directory == ".":
316
+ full_path = full_path.removeprefix("./")
317
+
318
+ file_paths.append(relative_path)
319
+
320
+ return file_paths
321
+
322
+ def extract_shebang_language(shebang: str) -> str:
323
+ """
324
+ Take a string like
325
+ #!/usr/bin/env python3
326
+ and return the language (python3)
327
+ """
328
+ parts = shebang.split()
329
+ # Check if the shebang string starts with '#!' and has at least two parts
330
+ # Check for #!/usr/bin/env python3 pattern
331
+ if "#!" in parts[0] and len(parts) > 1:
332
+
333
+ # Split the second part by '/' and check if it contains 'env'
334
+ if 'env' in parts[0]:
335
+ interpreter = parts[-1]
336
+ #return parts[-1] # The interpreter is the last part
337
+ if interpreter.startswith("python"):
338
+ return "Python"
339
+ else:
340
+ return interpreter
341
+
342
+ # Check for the following pattern
343
+ # #!/usr/local/bin/perl style pattern
344
+ else:
345
+ return shebang.split('/')[-1] # Otherwise, the interpreter is the last
346
+
347
+ return None
348
+
349
+ def get_language_edge_cases(file_path):
350
+ """
351
+ Handle edge cases where certain filenames are special file types
352
+ Seems to be a go thing ...
353
+ """
354
+ basename = os.path.basename(file_path)
355
+
356
+ if basename:
357
+ return LANGUAGE_BY_BASENAME.get(basename.lower())
358
+ else:
359
+ return None
360
+
361
+ def get_language(file_path):
362
+ """ Return the language of a file """
363
+ ext = get_fileext(file_path)
364
+ lang = None
365
+
366
+ lang_by_basename = get_language_edge_cases(file_path)
367
+ if lang_by_basename:
368
+ return lang_by_basename
369
+
370
+ if ext:
371
+ lang = get_extension_filetype(ext)
372
+
373
+ if lang:
374
+ return lang
375
+
376
+ shebang = check_shebang(file_path)
377
+
378
+ if shebang:
379
+ lang = get_shebang_language(shebang)
380
+
381
+ return lang
382
+
383
+ #def basename_check(file_path):
384
+ # """
385
+ # Return a guessed type based on the basename
386
+ # """
387
+
388
+ # return None
389
+
390
+ def extract_urls(text):
391
+ """
392
+ Find all HTTP/S URLs from a given string and return a list of URLs found.
393
+
394
+ """
395
+ url_pattern = re.compile(r'https?://[^\s\"\'\)]+')
396
+ urls = re.findall(url_pattern, text)
397
+ return urls
398
+
399
+ def extract_urls_from_file(file_path):
400
+ """
401
+ Extract URLs from a given file.
402
+
403
+ Args:
404
+ file_path (str): File to extract URLs from
405
+
406
+ Returns:
407
+ list: List of URLs found in the file
408
+
409
+ Raises:
410
+ FileNotFoundError: If the specified file does not exist
411
+ UnicodeDecodeError: If the file cannot be decoded as UTF-8
412
+ """
413
+ if not os.path.exists(file_path):
414
+ raise FileNotFoundError(f"File not found: {file_path}")
415
+
416
+ try:
417
+ with open(file_path, 'r', encoding='utf-8') as file:
418
+ content = file.read()
419
+ return extract_urls(content)
420
+ except UnicodeDecodeError as e:
421
+ raise UnicodeDecodeError(f"Unable to decode file {file_path} as UTF-8: {str(e)}")
422
+
423
+ def is_pip_requirements(filename: str) -> bool:
424
+ """
425
+ Returns True if `filename` matches typical pip requirements filenames.
426
+ Examples of matching filenames:
427
+ - requirements.txt
428
+ - requirements-dev.txt
429
+ - requirements-dev.in
430
+ - requirements.in
431
+ - requirements.dev.txt
432
+ """
433
+
434
+ PATTERN = re.compile(r'^requirements([-._a-zA-Z0-9]*)\.(txt|in)$')
435
+
436
+ return bool(PATTERN.match(filename))
@@ -0,0 +1,111 @@
1
+ # panopticas.py
2
+ import click
3
+ from prettytable import PrettyTable
4
+ import panopticas as ft
5
+
6
+ VERSION = "0.0.9"
7
+
8
+ @click.group()
9
+ def cli():
10
+ """Panopticas is a tool for identifying file types, code and git repositories.
11
+
12
+ In future, it will be possible identify external dependencies
13
+ (e.g. URLs, cloud providers)
14
+
15
+ For documentation on how commands run `panopticas COMMAND --help`.
16
+
17
+ See also https://panopticas.io/
18
+
19
+ """
20
+
21
+ @cli.command("assess")
22
+ @click.option('-unknown', is_flag=True, default=False, help="Show only files with an unknown language type.")
23
+ @click.argument('directory', required=False, type=click.Path(exists=True))
24
+ def assess(directory,unknown):
25
+ """Assess a directory."""
26
+ click.echo()
27
+ if directory:
28
+ click.echo(f'Assessing directory: {directory}')
29
+ else:
30
+ click.echo('Assessing current directory.')
31
+ directory = "."
32
+ files = ft.identify_files(directory)
33
+ click.echo(f'Found {len(files)} files.\n')
34
+ table = PrettyTable()
35
+ table.field_names = ["File", "Language", "Meta"]
36
+ table.align["File"] = "l"
37
+ table.align["Language"] = "l"
38
+ table.align["Meta"] = "l"
39
+
40
+ for file, file_type in files.items():
41
+ meta = ft.get_filename_metatypes(file) if ft.get_filename_metatypes(file) else ""
42
+ if meta:
43
+ meta = ", ".join(meta)
44
+ # with "unknown", we only include files with unknown language types (e.g. None)
45
+ if unknown:
46
+ if file_type is None:
47
+ #print(f"File: {file} is of unknown language type")
48
+ table.add_row([file, file_type, meta])
49
+ else:
50
+ # Default is we add it to the table
51
+ table.add_row([file, file_type, meta])
52
+
53
+ print(table,"\n")
54
+
55
+ @cli.command("file")
56
+ @click.argument('file', required=True,type=click.Path(exists=True))
57
+ def identify(file):
58
+ """Assess a filetype."""
59
+ click.echo(f'\nAssessing filetype for file {file}')
60
+ click.echo()
61
+ table = PrettyTable()
62
+ table.field_names = ["Method", "Result"]
63
+ table.align["Method"] = "l"
64
+ table.align["Result"] = "l"
65
+
66
+ table.add_row(["File extenion", ft.get_fileext(file)])
67
+ table.add_row(["File type", ft.get_extension_filetype(ft.get_fileext(file))])
68
+ shebang = ft.check_shebang(file)
69
+ table.add_row(["Shebang", shebang])
70
+ table.add_row(["Shebang Language", ft.extract_shebang_language(shebang) if shebang else None])
71
+ table.add_row(["Meta", ft.get_filename_metatypes(file)])
72
+
73
+ urls = []
74
+
75
+ urls = ft.extract_urls_from_file(file)
76
+ table.add_row(["URLs", '\n'.join(urls)])
77
+
78
+ print(table)
79
+ print()
80
+
81
+ @cli.command("urls")
82
+ @click.option('-all-files', is_flag=True, default=False, help="Show all files, no gitignore.")
83
+ @click.argument('directory', required=True, type=click.Path(exists=True))
84
+ def find_urls(directory,all_files):
85
+ """
86
+ Find and show urls for all files in a given directory.
87
+ """
88
+ files = ft.find_files(directory,all_files=all_files)
89
+
90
+ table = PrettyTable()
91
+ table.field_names = ["Filename", "URLs"]
92
+ table.align["Filename"] = "l"
93
+ table.align["URLs"] = "l"
94
+
95
+ # ft.find_files(directory)
96
+ for f in files:
97
+ urls = ft.extract_urls_from_file(f)
98
+ table.add_row([f, '\n'.join(urls)])
99
+
100
+ print(table)
101
+ print()
102
+
103
+
104
+ @cli.command("version")
105
+ def version():
106
+ """Print the version of Panopticas."""
107
+ click.echo(f"Panopticas CLI version {VERSION}")
108
+ click.echo(f"Panopticas v{ft.VERSION}")
109
+
110
+ if __name__ == '__main__':
111
+ cli()