python-cath 0.2.5__py3-none-any.whl → 0.4.0__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.
python_cath/__init__.py CHANGED
@@ -1,11 +1,7 @@
1
- # type: ignore[attr-defined]
1
+ # mypy: disable-error-code="attr-defined"
2
2
  """Cat files w/ headers"""
3
3
 
4
- try:
5
- from importlib.metadata import version, PackageNotFoundError
6
- except ImportError: # pragma: no cover
7
- from importlib_metadata import version, PackageNotFoundError
8
-
4
+ from importlib.metadata import PackageNotFoundError, version
9
5
 
10
6
  try:
11
7
  __version__ = version(__name__)
python_cath/__main__.py CHANGED
@@ -1,23 +1,23 @@
1
- # type: ignore[attr-defined]
1
+ # mypy: disable-error-code="attr-defined"
2
2
 
3
- import random
4
- from enum import Enum
5
- from typing import List, Optional
3
+ from typing import List
6
4
 
7
5
  import typer
8
- from rich.console import Console
9
-
10
6
  from python_cath import __version__
11
7
  from python_cath.concat import concat
12
- from python_cath.example import hello
8
+ from rich.console import Console
13
9
 
14
10
  app = typer.Typer(
15
- name="python-cath", help="Cat files w/ headers", add_completion=False,
11
+ name="python-cath",
12
+ help="Cat files w/ headers",
13
+ add_completion=False,
14
+ invoke_without_command=True,
15
+ no_args_is_help=True,
16
16
  )
17
17
  console = Console()
18
18
 
19
19
 
20
- def version_callback(value: bool):
20
+ def version_callback(value: bool) -> None:
21
21
  """Prints the version of the package."""
22
22
  if value:
23
23
  console.print(
@@ -29,7 +29,9 @@ def version_callback(value: bool):
29
29
  @app.command()
30
30
  def main(
31
31
  file_list: List[str] = typer.Argument(...),
32
- output_file: str = typer.Argument(...),
33
- ):
34
- """Prints a greeting for a giving name."""
32
+ output_file: str = typer.Option(
33
+ ..., "--output", "-o", help="Output file path"
34
+ ),
35
+ ) -> None:
36
+ """Concatenate CSV files with a shared header into output_file."""
35
37
  concat(file_list, output_file)
python_cath/concat.py CHANGED
@@ -1,7 +1,49 @@
1
1
  import os
2
+ import shutil
2
3
 
3
4
 
4
5
  def concat(file_list, output_file):
5
- os.system(f"head -1 {file_list[0]} > {output_file}")
6
+ """Concatenate CSV files that share the same header.
7
+
8
+ Args:
9
+ file_list: Paths to input files.
10
+ output_file: Path to write the concatenated output.
11
+
12
+ Raises:
13
+ ValueError: If no input files are provided, any file has an empty
14
+ header, an input file path collides with the output file, or
15
+ file headers differ.
16
+ """
17
+ file_list = list(file_list)
18
+ if not file_list:
19
+ raise ValueError("file_list must not be empty")
20
+
21
+ real_output = os.path.realpath(output_file)
6
22
  for afile in file_list:
7
- os.system(f"tail -n +2 {afile} >> {output_file}")
23
+ if os.path.realpath(afile) == real_output:
24
+ raise ValueError(
25
+ f"Input file {afile!r} is the same path as the output file"
26
+ )
27
+
28
+ # First pass: read and validate headers one file at a time
29
+ file_headers = {}
30
+ for afile in file_list:
31
+ with open(afile, encoding="utf-8") as fh:
32
+ header = fh.readline().rstrip("\n")
33
+ if not header:
34
+ raise ValueError(f"File {afile!r} has an empty or missing header")
35
+ file_headers[afile] = header
36
+
37
+ unique_headers = set(file_headers.values())
38
+ if len(unique_headers) > 1:
39
+ detail = ", ".join(f"{f!r}: {h!r}" for f, h in file_headers.items())
40
+ raise ValueError(f"Headers do not match: {detail}")
41
+
42
+ # Second pass: write output one file at a time
43
+ shared_header = next(iter(unique_headers))
44
+ with open(output_file, "w", encoding="utf-8") as out:
45
+ out.write(shared_header + "\n")
46
+ for afile in file_list:
47
+ with open(afile, encoding="utf-8") as fh:
48
+ fh.readline() # skip header
49
+ shutil.copyfileobj(fh, out)
@@ -1,24 +1,27 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python-cath
3
- Version: 0.2.5
3
+ Version: 0.4.0
4
4
  Summary: Cat files w/ headers
5
- Home-page: https://github.com/samesense/python-cath
6
5
  License: MIT
6
+ License-File: LICENSE
7
7
  Author: samesense
8
8
  Author-email: candptea@gmail.com
9
- Requires-Python: >=3.7,<4.0
9
+ Requires-Python: >=3.10,<4.0
10
10
  Classifier: Development Status :: 3 - Alpha
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: License :: OSI Approved :: MIT License
13
13
  Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.7
16
- Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
17
20
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
- Requires-Dist: importlib_metadata (>=1.6.0,<2.0.0); python_version < "3.8"
19
- Requires-Dist: pre-commit (>=2.5.1,<3.0.0)
20
- Requires-Dist: rich (>=2.2.2,<3.0.0)
21
- Requires-Dist: typer[all] (>=0.2.1,<0.3.0)
21
+ Requires-Dist: pre-commit (>=3.0.0,<4.0.0)
22
+ Requires-Dist: rich (>=10.0.0)
23
+ Requires-Dist: typer[all] (>=0.9.0,<0.10.0)
24
+ Project-URL: Homepage, https://github.com/samesense/python-cath
22
25
  Project-URL: Repository, https://github.com/samesense/python-cath
23
26
  Description-Content-Type: text/markdown
24
27
 
@@ -58,7 +61,7 @@ python-cath --help
58
61
  ```
59
62
 
60
63
  ```bash
61
- python-cath file1 file2 outfile
64
+ python-cath file1 file2 --output outfile
62
65
  ```
63
66
 
64
67
  or if installed with `Poetry`:
@@ -68,7 +71,7 @@ poetry run python-cath --help
68
71
  ```
69
72
 
70
73
  ```bash
71
- poetry run python-cath file1 file2 outfile
74
+ poetry run python-cath file1 file2 --output outfile
72
75
  ```
73
76
 
74
77
  ### Makefile usage
@@ -0,0 +1,10 @@
1
+ python_cath/__init__.py,sha256=jWtDUGUgV39IeDqwlQ9ziOy9ceofcf1hsppd2C_t9Yw,250
2
+ python_cath/__main__.py,sha256=SKkaB4X6hUcd10OSsqM65Rj9QGiOt2jK1K-Cxs2FN2g,902
3
+ python_cath/concat.py,sha256=RYSWSePSbdWI9JlzFlIVtoa7rH1-LmiruorjDc9jKZY,1731
4
+ python_cath/example.py,sha256=LGLU6ShWSJtu2zPbGXmQJw5ktJkNlGnRhFjY8OTlePM,366
5
+ python_cath/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ python_cath-0.4.0.dist-info/METADATA,sha256=13QGcyH60F0hUw-CAeztY5eg8-AMEl_vAp8xYszxfQ4,7692
7
+ python_cath-0.4.0.dist-info/WHEEL,sha256=Vz2fHgx6HFtSwhs8KvkHLqH5Ea4w1_rner5uNVGCeIE,88
8
+ python_cath-0.4.0.dist-info/entry_points.txt,sha256=CKAIA5eO7sx0McMvZxxXm01CYasNWeOfB3MWbkNcP7I,56
9
+ python_cath-0.4.0.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ python_cath-0.4.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry 1.0.10
2
+ Generator: poetry-core 2.3.2
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,10 +0,0 @@
1
- python_cath/__init__.py,sha256=HIJeYPnh_ZqTjg5-1pf6_VFBPCMeva5APJJkOfYH2wc,352
2
- python_cath/__main__.py,sha256=NxUhgsLFy8DOpNs7Kq0DJS3lTyIfqqdMH9FgWSNAzRk,812
3
- python_cath/concat.py,sha256=ytMyX5tBezA4b604dk4DQrMEGMKwLcYPAIJwSwFNO9k,191
4
- python_cath/example.py,sha256=LGLU6ShWSJtu2zPbGXmQJw5ktJkNlGnRhFjY8OTlePM,366
5
- python_cath/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- python_cath-0.2.5.dist-info/entry_points.txt,sha256=CKAIA5eO7sx0McMvZxxXm01CYasNWeOfB3MWbkNcP7I,56
7
- python_cath-0.2.5.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- python_cath-0.2.5.dist-info/WHEEL,sha256=Q99itqWYDhV793oHzqzi24q7L7Kdiz6cb55YDfTXphE,84
9
- python_cath-0.2.5.dist-info/METADATA,sha256=pdUUy0Njms0Lt1otIoT0ZrxK4KKsjgkDGBNgx8yUpp0,7564
10
- python_cath-0.2.5.dist-info/RECORD,,