lawkit-python 2.1.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.
lawkit/__init__.py ADDED
@@ -0,0 +1,60 @@
1
+ """
2
+ lawkit: Python wrapper for the lawkit CLI tool
3
+
4
+ This package provides a Python interface to the lawkit CLI tool for statistical
5
+ law analysis including Benford's Law, Pareto principle, Zipf's Law, Normal distribution,
6
+ and Poisson distribution analysis. Perfect for fraud detection, data quality assessment,
7
+ and statistical analysis.
8
+ """
9
+
10
+ from .lawkit import (
11
+ analyze_benford,
12
+ analyze_pareto,
13
+ analyze_zipf,
14
+ analyze_normal,
15
+ analyze_poisson,
16
+ compare_laws,
17
+ generate_data,
18
+ analyze_string,
19
+ is_lawkit_available,
20
+ get_version,
21
+ selftest,
22
+ LawkitOptions,
23
+ LawkitResult,
24
+ LawkitError,
25
+ Format,
26
+ OutputFormat,
27
+ LawType,
28
+ )
29
+
30
+ # For backward compatibility and convenience
31
+ from .compat import run_lawkit
32
+
33
+ __version__ = "2.1.0"
34
+ __all__ = [
35
+ # Main analysis functions
36
+ "analyze_benford",
37
+ "analyze_pareto",
38
+ "analyze_zipf",
39
+ "analyze_normal",
40
+ "analyze_poisson",
41
+ "compare_laws",
42
+
43
+ # Utility functions
44
+ "generate_data",
45
+ "analyze_string",
46
+ "is_lawkit_available",
47
+ "get_version",
48
+ "selftest",
49
+
50
+ # Types and classes
51
+ "LawkitOptions",
52
+ "LawkitResult",
53
+ "LawkitError",
54
+ "Format",
55
+ "OutputFormat",
56
+ "LawType",
57
+
58
+ # Backward compatibility
59
+ "run_lawkit",
60
+ ]
lawkit/compat.py ADDED
@@ -0,0 +1,204 @@
1
+ """
2
+ Backward compatibility module for lawkit-python
3
+
4
+ This module provides compatibility functions for users migrating from
5
+ other statistical analysis tools or expecting different API patterns.
6
+ """
7
+
8
+ import subprocess
9
+ import platform
10
+ from pathlib import Path
11
+ from typing import List, Union
12
+
13
+
14
+ class LawkitProcess:
15
+ """Compatibility class that mimics subprocess.CompletedProcess"""
16
+ def __init__(self, returncode: int, stdout: str, stderr: str):
17
+ self.returncode = returncode
18
+ self.stdout = stdout
19
+ self.stderr = stderr
20
+
21
+
22
+ def run_lawkit(args: List[str], input_data: Union[str, None] = None) -> LawkitProcess:
23
+ """
24
+ Run lawkit command with arguments (legacy compatibility function)
25
+
26
+ Args:
27
+ args: Command line arguments (without 'lawkit' prefix)
28
+ input_data: Optional input data to pass via stdin
29
+
30
+ Returns:
31
+ LawkitProcess object with returncode, stdout, stderr
32
+
33
+ Examples:
34
+ >>> result = run_lawkit(["benf", "data.csv"])
35
+ >>> if result.returncode == 0:
36
+ ... print("Analysis successful")
37
+ ... print(result.stdout)
38
+ ... else:
39
+ ... print("Analysis failed")
40
+ ... print(result.stderr)
41
+
42
+ >>> # With input data
43
+ >>> csv_data = "amount\\n123\\n456\\n789"
44
+ >>> result = run_lawkit(["benf", "-"], input_data=csv_data)
45
+ """
46
+ # Get the path to the lawkit binary
47
+ package_dir = Path(__file__).parent.parent.parent
48
+ binary_name = "lawkit.exe" if platform.system() == "Windows" else "lawkit"
49
+ local_binary_path = package_dir / "bin" / binary_name
50
+
51
+ if local_binary_path.exists():
52
+ lawkit_path = str(local_binary_path)
53
+ else:
54
+ lawkit_path = "lawkit"
55
+
56
+ try:
57
+ # Run the command
58
+ result = subprocess.run(
59
+ [lawkit_path] + args,
60
+ input=input_data,
61
+ capture_output=True,
62
+ text=True,
63
+ timeout=300 # 5 minute timeout
64
+ )
65
+
66
+ return LawkitProcess(
67
+ returncode=result.returncode,
68
+ stdout=result.stdout,
69
+ stderr=result.stderr
70
+ )
71
+
72
+ except subprocess.TimeoutExpired:
73
+ return LawkitProcess(
74
+ returncode=-1,
75
+ stdout="",
76
+ stderr="Command timed out after 5 minutes"
77
+ )
78
+ except FileNotFoundError:
79
+ return LawkitProcess(
80
+ returncode=-1,
81
+ stdout="",
82
+ stderr="lawkit command not found. Please install lawkit CLI tool."
83
+ )
84
+ except Exception as e:
85
+ return LawkitProcess(
86
+ returncode=-1,
87
+ stdout="",
88
+ stderr=f"Error running lawkit: {e}"
89
+ )
90
+
91
+
92
+ def run_benford_analysis(file_path: str, **kwargs) -> LawkitProcess:
93
+ """
94
+ Legacy function for Benford's Law analysis
95
+
96
+ Args:
97
+ file_path: Path to input file
98
+ **kwargs: Additional options (format, output, etc.)
99
+
100
+ Returns:
101
+ LawkitProcess object
102
+
103
+ Examples:
104
+ >>> result = run_benford_analysis("data.csv", format="csv", output="json")
105
+ """
106
+ args = ["benf", file_path]
107
+
108
+ if "format" in kwargs:
109
+ args.extend(["--format", kwargs["format"]])
110
+
111
+ if "output" in kwargs:
112
+ args.extend(["--output", kwargs["output"]])
113
+
114
+ if "min_count" in kwargs:
115
+ args.extend(["--min-count", str(kwargs["min_count"])])
116
+
117
+ if "threshold" in kwargs:
118
+ args.extend(["--threshold", str(kwargs["threshold"])])
119
+
120
+ if kwargs.get("verbose", False):
121
+ args.append("--verbose")
122
+
123
+ if kwargs.get("optimize", False):
124
+ args.append("--optimize")
125
+
126
+ return run_lawkit(args)
127
+
128
+
129
+ def run_pareto_analysis(file_path: str, **kwargs) -> LawkitProcess:
130
+ """
131
+ Legacy function for Pareto principle analysis
132
+
133
+ Args:
134
+ file_path: Path to input file
135
+ **kwargs: Additional options
136
+
137
+ Returns:
138
+ LawkitProcess object
139
+
140
+ Examples:
141
+ >>> result = run_pareto_analysis("sales.csv", gini_coefficient=True)
142
+ """
143
+ args = ["pareto", file_path]
144
+
145
+ if "format" in kwargs:
146
+ args.extend(["--format", kwargs["format"]])
147
+
148
+ if "output" in kwargs:
149
+ args.extend(["--output", kwargs["output"]])
150
+
151
+ if kwargs.get("gini_coefficient", False):
152
+ args.append("--gini-coefficient")
153
+
154
+ if "percentiles" in kwargs:
155
+ args.extend(["--percentiles", kwargs["percentiles"]])
156
+
157
+ if kwargs.get("business_analysis", False):
158
+ args.append("--business-analysis")
159
+
160
+ if kwargs.get("verbose", False):
161
+ args.append("--verbose")
162
+
163
+ return run_lawkit(args)
164
+
165
+
166
+ def check_lawkit_installation() -> bool:
167
+ """
168
+ Check if lawkit is properly installed
169
+
170
+ Returns:
171
+ True if lawkit is available, False otherwise
172
+
173
+ Examples:
174
+ >>> if not check_lawkit_installation():
175
+ ... print("Please install lawkit first")
176
+ ... exit(1)
177
+ """
178
+ result = run_lawkit(["--version"])
179
+ return result.returncode == 0
180
+
181
+
182
+ def get_lawkit_help(subcommand: str = None) -> str:
183
+ """
184
+ Get help text for lawkit or a specific subcommand
185
+
186
+ Args:
187
+ subcommand: Optional subcommand name
188
+
189
+ Returns:
190
+ Help text as string
191
+
192
+ Examples:
193
+ >>> help_text = get_lawkit_help()
194
+ >>> print(help_text)
195
+
196
+ >>> benf_help = get_lawkit_help("benf")
197
+ >>> print(benf_help)
198
+ """
199
+ if subcommand:
200
+ result = run_lawkit([subcommand, "--help"])
201
+ else:
202
+ result = run_lawkit(["--help"])
203
+
204
+ return result.stdout if result.returncode == 0 else result.stderr
lawkit/installer.py ADDED
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env python3
2
+ """Binary installer for lawkit."""
3
+
4
+ import os
5
+ import platform
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ import tarfile
10
+ import tempfile
11
+ import urllib.request
12
+ import zipfile
13
+ from pathlib import Path
14
+
15
+ LAWKIT_VERSION = "2.1.0"
16
+
17
+
18
+ def get_platform_info():
19
+ """Get platform-specific download information."""
20
+ system = platform.system().lower()
21
+ machine = platform.machine().lower()
22
+
23
+ if system == "windows":
24
+ return "lawkit-windows-x86_64.zip", "lawkit.exe"
25
+ elif system == "darwin":
26
+ if machine in ["arm64", "aarch64"]:
27
+ return "lawkit-macos-aarch64.tar.gz", "lawkit"
28
+ else:
29
+ return "lawkit-macos-x86_64.tar.gz", "lawkit"
30
+ elif system == "linux":
31
+ if machine in ["arm64", "aarch64"]:
32
+ return "lawkit-linux-aarch64.tar.gz", "lawkit"
33
+ else:
34
+ return "lawkit-linux-x86_64.tar.gz", "lawkit"
35
+ else:
36
+ raise RuntimeError(f"Unsupported platform: {system}-{machine}")
37
+
38
+
39
+ def download_file(url: str, dest_path: Path) -> None:
40
+ """Download a file from URL to destination."""
41
+ print(f"Downloading lawkit binary from {url}...")
42
+
43
+ try:
44
+ with urllib.request.urlopen(url) as response:
45
+ with open(dest_path, 'wb') as dest_file:
46
+ shutil.copyfileobj(response, dest_file)
47
+ except Exception as e:
48
+ raise RuntimeError(f"Failed to download {url}: {e}")
49
+
50
+
51
+ def extract_archive(archive_path: Path, extract_dir: Path) -> None:
52
+ """Extract archive file."""
53
+ print("Extracting binary...")
54
+
55
+ if archive_path.suffix == '.zip':
56
+ with zipfile.ZipFile(archive_path, 'r') as zip_file:
57
+ zip_file.extractall(extract_dir)
58
+ elif archive_path.name.endswith('.tar.gz'):
59
+ with tarfile.open(archive_path, 'r:gz') as tar_file:
60
+ tar_file.extractall(extract_dir)
61
+ else:
62
+ raise RuntimeError(f"Unsupported archive format: {archive_path}")
63
+
64
+
65
+ def main():
66
+ """Main function to download and install lawkit binary."""
67
+ try:
68
+ # Get the package directory (where this script will be installed)
69
+ if hasattr(sys, '_MEIPASS'):
70
+ # If running from PyInstaller bundle
71
+ package_dir = Path(sys._MEIPASS).parent
72
+ else:
73
+ # Normal installation
74
+ package_dir = Path(__file__).parent.parent.parent
75
+
76
+ bin_dir = package_dir / "bin"
77
+
78
+ # Get platform info
79
+ archive_name, binary_name = get_platform_info()
80
+ binary_path = bin_dir / binary_name
81
+
82
+ # Skip download if binary already exists
83
+ if binary_path.exists():
84
+ print("lawkit binary already exists, skipping download.")
85
+ return 0
86
+
87
+ # Create bin directory
88
+ bin_dir.mkdir(exist_ok=True)
89
+
90
+ # Download URL
91
+ download_url = f"https://github.com/kako-jun/lawkit/releases/download/v{LAWKIT_VERSION}/{archive_name}"
92
+
93
+ # Download to temporary file
94
+ with tempfile.TemporaryDirectory() as temp_dir:
95
+ temp_path = Path(temp_dir)
96
+ archive_path = temp_path / archive_name
97
+
98
+ download_file(download_url, archive_path)
99
+ extract_archive(archive_path, bin_dir)
100
+
101
+ # Make binary executable on Unix systems
102
+ if platform.system() != "Windows":
103
+ binary_path.chmod(0o755)
104
+
105
+ print(f"✅ lawkit binary installed successfully at {binary_path}")
106
+
107
+ # Test the binary
108
+ try:
109
+ result = subprocess.run([str(binary_path), "--version"],
110
+ capture_output=True, text=True, timeout=10)
111
+ if result.returncode == 0:
112
+ print(f"✅ lawkit binary is working correctly")
113
+ print(f" Version: {result.stdout.strip()}")
114
+ else:
115
+ print(f"⚠️ lawkit binary installed but version check failed")
116
+ except (subprocess.TimeoutExpired, subprocess.CalledProcessError) as e:
117
+ print(f"⚠️ lawkit binary installed but verification failed: {e}")
118
+
119
+ return 0
120
+
121
+ except Exception as error:
122
+ print(f"❌ Failed to download lawkit binary: {error}")
123
+ print("You may need to install lawkit manually from: https://github.com/kako-jun/lawkit/releases")
124
+ print("Or build from source: https://github.com/kako-jun/lawkit")
125
+ # Don't fail the installation, just warn
126
+ return 0
127
+
128
+
129
+ if __name__ == "__main__":
130
+ sys.exit(main())