noctyra 0.1.0__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.
Files changed (37) hide show
  1. noctyra-0.1.0/.claude/settings.local.json +9 -0
  2. noctyra-0.1.0/.github/dependabot.yml +11 -0
  3. noctyra-0.1.0/.github/workflows/ci.yml +35 -0
  4. noctyra-0.1.0/.gitignore +14 -0
  5. noctyra-0.1.0/.pre-commit-config.yaml +22 -0
  6. noctyra-0.1.0/.python-version +1 -0
  7. noctyra-0.1.0/CLAUDE.md +50 -0
  8. noctyra-0.1.0/LICENSE +21 -0
  9. noctyra-0.1.0/Makefile +21 -0
  10. noctyra-0.1.0/PKG-INFO +98 -0
  11. noctyra-0.1.0/README.md +69 -0
  12. noctyra-0.1.0/SECURITY.md +21 -0
  13. noctyra-0.1.0/assets/demo.gif +0 -0
  14. noctyra-0.1.0/classes/Context.py +75 -0
  15. noctyra-0.1.0/classes/Function.py +7 -0
  16. noctyra-0.1.0/classes/Variable.py +35 -0
  17. noctyra-0.1.0/classes/__init__.py +5 -0
  18. noctyra-0.1.0/main.py +100 -0
  19. noctyra-0.1.0/make.bat +21 -0
  20. noctyra-0.1.0/pyproject.toml +53 -0
  21. noctyra-0.1.0/tests/conftest.py +4 -0
  22. noctyra-0.1.0/tests/test_transformers.py +264 -0
  23. noctyra-0.1.0/transformers/Attributes.py +36 -0
  24. noctyra-0.1.0/transformers/BaseTransformer.py +46 -0
  25. noctyra-0.1.0/transformers/CompareSimplifier.py +55 -0
  26. noctyra-0.1.0/transformers/Constants.py +42 -0
  27. noctyra-0.1.0/transformers/DeadCodeEleminator.py +34 -0
  28. noctyra-0.1.0/transformers/Functions.py +33 -0
  29. noctyra-0.1.0/transformers/ListComp.py +99 -0
  30. noctyra-0.1.0/transformers/NameReplacer.py +19 -0
  31. noctyra-0.1.0/transformers/Pipeline.py +76 -0
  32. noctyra-0.1.0/transformers/__init__.py +19 -0
  33. noctyra-0.1.0/transformers/safe_eval.py +358 -0
  34. noctyra-0.1.0/utils/__init__.py +1 -0
  35. noctyra-0.1.0/utils/logger.py +14 -0
  36. noctyra-0.1.0/utils/shared.py +95 -0
  37. noctyra-0.1.0/uv.lock +721 -0
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(timeout 30 time python main.py /home/lulu/Documents/noctyra_security_audit/poc_02_dos_bigint.py --output /home/lulu/Documents/noctyra_security_audit/outputs/poc_02_out.py)",
5
+ "Bash(echo \"EXIT:$?\")",
6
+ "Bash(timeout 60 bash -c \"time uv run python main.py /home/lulu/Documents/noctyra_security_audit/poc_01_dos_listcomp.py --output /home/lulu/Documents/noctyra_security_audit/outputs/poc_01_out.py\")"
7
+ ]
8
+ }
9
+ }
@@ -0,0 +1,11 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "uv"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+
8
+ - package-ecosystem: "github-actions"
9
+ directory: "/"
10
+ schedule:
11
+ interval: "weekly"
@@ -0,0 +1,35 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v6
14
+
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v7
17
+ with:
18
+ enable-cache: true
19
+
20
+ - name: Set up Python
21
+ run: uv python install
22
+
23
+ - name: Install dependencies
24
+ run: uv sync --all-extras --dev
25
+
26
+ - name: Run Ruff (Linter)
27
+ run: uv run ruff check .
28
+
29
+ - name: Run Mypy (Type Check)
30
+ run: uv run mypy . --ignore-missing-imports
31
+
32
+ - name: Run Tests
33
+ run: |
34
+ export PYTHONPATH=$PYTHONPATH:.
35
+ uv run pytest tests/
@@ -0,0 +1,14 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+
13
+ # Personal files
14
+ .bk
@@ -0,0 +1,22 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v5.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-added-large-files
9
+
10
+ - repo: https://github.com/astral-sh/ruff-pre-commit
11
+ rev: v0.9.9
12
+ hooks:
13
+ - id: ruff
14
+ args: [ --fix ]
15
+ - id: ruff-format
16
+
17
+ - repo: https://github.com/pre-commit/mirrors-mypy
18
+ rev: v1.15.0
19
+ hooks:
20
+ - id: mypy
21
+ additional_dependencies: [tokenize-rt==5.2.0, types-requests]
22
+ args: [--ignore-missing-imports]
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,50 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Commands
6
+
7
+ Use `uv` for all package management:
8
+
9
+ ```bash
10
+ uv sync # Install basic dependencies
11
+ uv sync --all-extras --dev # Install all dependencies including dev tools
12
+ ```
13
+
14
+ | Task | Command |
15
+ |------|---------|
16
+ | Run | `python main.py <input_file> [--output out.py] [--iterations N] [--debug]` |
17
+ | Test | `make test` → `uv run pytest tests/` |
18
+ | Lint | `make lint` → `uv run ruff check .` |
19
+ | Format | `make format` → `uv run black . && uv run ruff check . --fix` |
20
+ | Typecheck | `make typecheck` → `uv run mypy . --ignore-missing-imports` |
21
+ | Single test | `uv run pytest tests/test_transformers.py::TestName -v` |
22
+
23
+ ## Architecture
24
+
25
+ Noctyra is an AST-based Python deobfuscation/transformation framework. It parses a Python source file, runs a pipeline of AST transformers iteratively until no changes occur (or a fixed iteration count), then formats and writes the result.
26
+
27
+ **Execution flow:**
28
+
29
+ ```
30
+ main.py → ast.parse() → TransformerPipeline → black.format_str() → output file
31
+ ```
32
+
33
+ **TransformerPipeline** (`transformers/pipeline.py`) runs five transformers in sequence each iteration:
34
+
35
+ 1. **ConstsTransformer** (`constants.py`) — constant folding, encoding/compression calls (base64, zlib, brotli, etc.)
36
+ 2. **BasicFunctions** (`functions.py`) — `exec()` unrolling, evaluation of `ord`, `chr`, `int`, `str`, `bytes`, etc.
37
+ 3. **BasicAttributes** (`attributes.py`) — string/bytes method resolution (`.decode`, `.join`, `.hex`, etc.) when all arguments are constant
38
+ 4. **LCTransformer** (`listcomp.py`) — folds list comprehensions and generators into literal lists
39
+ 5. **ConditionSimplifier** (`condsimplifier.py`) — simplifies comparisons and chained boolean expressions
40
+
41
+ **SafeEval** (`transformers/safe_eval.py`) is the core evaluation engine used by most transformers. It walks AST nodes to evaluate constant expressions safely, with hard resource limits (100,000 bytes max allocation, ±100,000 numeric range, whitelist of allowed operators/functions).
42
+
43
+ **SharedUtils** (`utils/shared.py`) holds maps reused across transformers: operator functions, safe built-ins, and encoding/compression handlers (including third-party: brotli, zstd, blosc).
44
+
45
+ ## Key constraints
46
+
47
+ - **Python ≥ 3.14** required (uses newer AST APIs).
48
+ - SafeEval is intentionally restricted — never expand it without considering bypass risks. For untrusted input, run in an external sandbox.
49
+ - Transformers must be **idempotent**: applying them twice should produce the same result as once.
50
+ - Pre-commit hooks run ruff (lint + format) and mypy automatically on commit.
noctyra-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 0p4n1k <0p4n1k@mailfence.com>
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.
noctyra-0.1.0/Makefile ADDED
@@ -0,0 +1,21 @@
1
+ .PHONY: test lint format typecheck help
2
+
3
+ help:
4
+ @echo "Noctyra Development Commands"
5
+ @echo " test Run unit tests"
6
+ @echo " lint Check code style with ruff"
7
+ @echo " format Format code with black and ruff"
8
+ @echo " typecheck Run mypy type checking"
9
+
10
+ test:
11
+ uv run pytest tests/
12
+
13
+ lint:
14
+ uv run ruff check .
15
+
16
+ format:
17
+ uv run black .
18
+ uv run ruff check . --fix
19
+
20
+ typecheck:
21
+ uv run mypy . --ignore-missing-imports
noctyra-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,98 @@
1
+ Metadata-Version: 2.4
2
+ Name: noctyra
3
+ Version: 0.1.0
4
+ Summary: A generic static Python deobfuscator based on AST transformations
5
+ Project-URL: Homepage, https://github.com/0p4n1k/Noctyra
6
+ Project-URL: Repository, https://github.com/0p4n1k/Noctyra
7
+ Project-URL: Issues, https://github.com/0p4n1k/Noctyra/issues
8
+ Author-email: 0p4n1k <0p4n1k@mailfence.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: ast,deobfuscator,python,reverse-engineering,static-analysis
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
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
20
+ Classifier: Topic :: Security
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: black>=26.3.1
24
+ Requires-Dist: blosc>=1.11.4
25
+ Requires-Dist: brotli>=1.2.0
26
+ Requires-Dist: rich>=14.3.3
27
+ Requires-Dist: zstd>=1.5.7.3
28
+ Description-Content-Type: text/markdown
29
+
30
+ # Noctyra
31
+
32
+ ![Noctyra Demo](assets/demo.gif)
33
+
34
+ ![Python Version](https://img.shields.io/badge/python-3.14%2B-blue)
35
+ ![Status](https://img.shields.io/badge/status-experimental-orange)
36
+
37
+ Noctyra is an AST-based framework designed for code transformation and deobfuscation. It provides a modular pipeline to analyze and simplify Python source code by resolving complex expressions and logic.
38
+
39
+ ## Overview
40
+
41
+ The project aims to provide a flexible environment for processing Python's Abstract Syntax Tree. By employing a sequence of pluggable transformers, Noctyra can:
42
+
43
+ - Simplify nested logic and conditions.
44
+ - Resolve static values and common encoding patterns.
45
+ - Unroll dynamic execution blocks into readable code.
46
+ - Optimize and clean up obfuscated constructs.
47
+
48
+ ## Getting Started
49
+
50
+ ### Prerequisites
51
+
52
+ Ensure you have `uv` installed for dependency management.
53
+
54
+ ```bash
55
+ uv sync
56
+ ```
57
+
58
+ ### Running the Pipeline
59
+
60
+ To process a target script, use the following command:
61
+
62
+ ```bash
63
+ python main.py <input_file> [options]
64
+ ```
65
+
66
+ **Options:**
67
+ - `file`: Path to the input Python file (required).
68
+ - `--output`: File name for the transformed code (default: `out.py`).
69
+ - `--iterations`: Set a fixed number of transformation passes (default: `0` for auto-detect).
70
+ - `--max-iterations`: Limit the passes in auto mode (default: `100`).
71
+ - `--debug`: Enable verbose logging for debugging transformations. (look sick)
72
+
73
+ ## Development
74
+
75
+ This project uses `uv` for dependency management and a `Makefile` for common tasks.
76
+
77
+ ### Setup
78
+ ```bash
79
+ uv sync --all-extras --dev
80
+ ```
81
+
82
+ ### Quality Control
83
+ Before submitting a PR or pushing changes, ensure all checks pass:
84
+
85
+ - **Run Tests**: `make test`
86
+ - **Lint & Format**: `make format`
87
+ - **Type Check**: `make typecheck`
88
+
89
+ *(Windows users: These commands work automatically via `make.bat`.)*
90
+
91
+ ### CI/CD
92
+ Automated checks are performed on every push via GitHub Actions, including linting with Ruff, type checking with Mypy, and unit testing with Pytest.
93
+
94
+ ## Security Notice
95
+
96
+ Noctyra includes an internal evaluation engine with basic resource limits. However, when dealing with untrusted code, it is highly recommended to run the pipeline within an isolated environment (such as a container or sandbox) to prevent potential side effects or resource exhaustion.
97
+
98
+ ---
@@ -0,0 +1,69 @@
1
+ # Noctyra
2
+
3
+ ![Noctyra Demo](assets/demo.gif)
4
+
5
+ ![Python Version](https://img.shields.io/badge/python-3.14%2B-blue)
6
+ ![Status](https://img.shields.io/badge/status-experimental-orange)
7
+
8
+ Noctyra is an AST-based framework designed for code transformation and deobfuscation. It provides a modular pipeline to analyze and simplify Python source code by resolving complex expressions and logic.
9
+
10
+ ## Overview
11
+
12
+ The project aims to provide a flexible environment for processing Python's Abstract Syntax Tree. By employing a sequence of pluggable transformers, Noctyra can:
13
+
14
+ - Simplify nested logic and conditions.
15
+ - Resolve static values and common encoding patterns.
16
+ - Unroll dynamic execution blocks into readable code.
17
+ - Optimize and clean up obfuscated constructs.
18
+
19
+ ## Getting Started
20
+
21
+ ### Prerequisites
22
+
23
+ Ensure you have `uv` installed for dependency management.
24
+
25
+ ```bash
26
+ uv sync
27
+ ```
28
+
29
+ ### Running the Pipeline
30
+
31
+ To process a target script, use the following command:
32
+
33
+ ```bash
34
+ python main.py <input_file> [options]
35
+ ```
36
+
37
+ **Options:**
38
+ - `file`: Path to the input Python file (required).
39
+ - `--output`: File name for the transformed code (default: `out.py`).
40
+ - `--iterations`: Set a fixed number of transformation passes (default: `0` for auto-detect).
41
+ - `--max-iterations`: Limit the passes in auto mode (default: `100`).
42
+ - `--debug`: Enable verbose logging for debugging transformations. (look sick)
43
+
44
+ ## Development
45
+
46
+ This project uses `uv` for dependency management and a `Makefile` for common tasks.
47
+
48
+ ### Setup
49
+ ```bash
50
+ uv sync --all-extras --dev
51
+ ```
52
+
53
+ ### Quality Control
54
+ Before submitting a PR or pushing changes, ensure all checks pass:
55
+
56
+ - **Run Tests**: `make test`
57
+ - **Lint & Format**: `make format`
58
+ - **Type Check**: `make typecheck`
59
+
60
+ *(Windows users: These commands work automatically via `make.bat`.)*
61
+
62
+ ### CI/CD
63
+ Automated checks are performed on every push via GitHub Actions, including linting with Ruff, type checking with Mypy, and unit testing with Pytest.
64
+
65
+ ## Security Notice
66
+
67
+ Noctyra includes an internal evaluation engine with basic resource limits. However, when dealing with untrusted code, it is highly recommended to run the pipeline within an isolated environment (such as a container or sandbox) to prevent potential side effects or resource exhaustion.
68
+
69
+ ---
@@ -0,0 +1,21 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ The following versions of Noctyra are currently supported with security updates:
6
+
7
+ | Version | Supported
8
+ | ------- | ------------------
9
+ | 0.1.x | ✅
10
+
11
+ ## Reporting a Vulnerability
12
+
13
+ We take the security of Noctyra's `SafeEval` and AST transformation process seriously. If you find a way to achieve arbitrary code execution during the transformation phase (bypassing the evaluation guards), please report it responsibly.
14
+
15
+ **Do not open an issue on GitHub.** Please report vulnerabilities through the following method:
16
+
17
+ 1. Send an email to [0p4n1k@mailfence.com]
18
+ 2. Include a proof-of-concept (PoC) payload.
19
+ 3. Describe the impact and the environment (Python version, OS).
20
+
21
+ We will respond within 48 hours and coordinate a fix.
Binary file
@@ -0,0 +1,75 @@
1
+ from .Function import CustomFunction
2
+ from .Variable import Variable, supported_types
3
+ import ast
4
+
5
+
6
+ class Context:
7
+ def __init__(self, vars=None):
8
+ self.vars: list[Variable] = vars or []
9
+
10
+ def get(self, name: str):
11
+ for var in self.vars:
12
+ if var.name == name and var.has_value():
13
+ return var
14
+
15
+ return None
16
+
17
+ def has(self, name: str):
18
+ return any(var.name == name for var in self.vars)
19
+
20
+ def set(self, name: str, value):
21
+
22
+ for var in self.vars:
23
+ if var.name == name:
24
+ var.set(value=value)
25
+ return
26
+
27
+ self.vars.append(Variable(name=name, value=value))
28
+
29
+ def copy_with(self, vars: list[Variable]):
30
+ new = Context(vars=self.vars.copy())
31
+ new.vars.extend(vars)
32
+ return new
33
+
34
+ def invalidate(self, name: str) -> None:
35
+ self.vars = [v for v in self.vars if v.name != name]
36
+
37
+ @staticmethod
38
+ def from_tree(tree: ast.AST) -> "Context":
39
+ ctx = Context()
40
+
41
+ for node in ast.iter_child_nodes(tree):
42
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
43
+ continue
44
+
45
+ if isinstance(node, (ast.Assign, ast.AnnAssign, ast.NamedExpr)):
46
+ try:
47
+ if node.value is None:
48
+ continue
49
+ targets = (
50
+ node.targets if isinstance(node, ast.Assign) else [node.target]
51
+ )
52
+
53
+ if not all(isinstance(t, ast.Name) for t in targets):
54
+ continue
55
+ if isinstance(node.value, ast.Lambda):
56
+ value = CustomFunction(node.value.body, node.value.args)
57
+
58
+ else:
59
+ value = ast.literal_eval(node.value)
60
+
61
+ if isinstance(value, supported_types):
62
+ for target in targets:
63
+ if isinstance(target, ast.Name):
64
+ ctx.set(target.id, value)
65
+
66
+ except ValueError:
67
+ pass
68
+
69
+ elif isinstance(node, ast.AugAssign):
70
+ if not isinstance(node.target, ast.Name):
71
+ continue
72
+
73
+ ctx.invalidate(node.target.id)
74
+
75
+ return ctx
@@ -0,0 +1,7 @@
1
+ import ast
2
+
3
+
4
+ class CustomFunction:
5
+ def __init__(self, body: ast.AST, args: ast.arguments) -> None:
6
+ self.body = body
7
+ self.args = args
@@ -0,0 +1,35 @@
1
+ from .Function import CustomFunction
2
+
3
+ supported_types = int | float | str | bool | bytes | list | dict | set | CustomFunction
4
+ supported_iterator = str | bytes | dict | list | set
5
+
6
+
7
+ class Variable:
8
+ def __init__(self, name, value):
9
+
10
+ if not isinstance(name, str):
11
+ raise ValueError(f"Variable name must be a string. {name=}, {value=}")
12
+
13
+ if not isinstance(value, supported_types):
14
+ raise ValueError(f"Unsupported variable type. {name=}, {value=}")
15
+
16
+ self.name = name
17
+ self.value = value
18
+
19
+ def has_value(self):
20
+ return self.value is not None
21
+
22
+ def set(self, *, value):
23
+
24
+ if not isinstance(value, supported_types) and value is not None:
25
+ raise ValueError(
26
+ f"Unsupported variable type: {type(value).__name__}. {self.name=}, {value=}"
27
+ )
28
+
29
+ self.value = value
30
+
31
+ def get(self) -> supported_types:
32
+ return self.value
33
+
34
+ def __repr__(self):
35
+ return f"Variable(name={self.name}, value={self.value})"
@@ -0,0 +1,5 @@
1
+ from .Context import Context
2
+ from .Variable import Variable, supported_types
3
+ from .Function import CustomFunction
4
+
5
+ __all__ = ["Context", "Variable", "CustomFunction", "supported_types"]
noctyra-0.1.0/main.py ADDED
@@ -0,0 +1,100 @@
1
+ from transformers import (
2
+ ConstsTransformer,
3
+ BasicFunctions,
4
+ BasicAttributes,
5
+ LCTransformer,
6
+ ConditionSimplifier,
7
+ NameReplacer,
8
+ DeadCodeRemover,
9
+ )
10
+ from transformers import TransformerPipeline
11
+ from utils.logger import LOGGER
12
+ import argparse
13
+ import black
14
+ import ast
15
+
16
+
17
+ def main():
18
+ argparser = argparse.ArgumentParser(
19
+ description="Transform Python code using AST transformers."
20
+ )
21
+ argparser.add_argument("file", help="Path to the Python file to transform.")
22
+ argparser.add_argument(
23
+ "--iterations",
24
+ type=int,
25
+ default=0,
26
+ help="Number of iterations to apply the transformers. Default is 0 (auto).",
27
+ )
28
+ argparser.add_argument(
29
+ "--max-iterations",
30
+ type=int,
31
+ default=100,
32
+ help="Maximum iterations when using auto mode. Default is 100.",
33
+ )
34
+ argparser.add_argument(
35
+ "--max-depth",
36
+ type=int,
37
+ default=50,
38
+ help="Maximum depth when inspecting recursive function. Default is 50.",
39
+ )
40
+ argparser.add_argument(
41
+ "--max-allocation",
42
+ type=int,
43
+ default=100_000,
44
+ help="Maximum allocation to prevent DOS. Default is 100000.",
45
+ )
46
+ argparser.add_argument(
47
+ "--output",
48
+ type=str,
49
+ default="out.py",
50
+ help="Output file for the transformed code. Default is out.py.",
51
+ )
52
+ argparser.add_argument("--debug", action="store_true", help="Enable debug logging.")
53
+ argparser.add_argument(
54
+ "--stdout", action="store_true", help="Output transformed code to stdout."
55
+ )
56
+
57
+ args = argparser.parse_args()
58
+
59
+ if args.debug:
60
+ LOGGER.setLevel("DEBUG")
61
+
62
+ with open(args.file, "r", encoding="utf-8") as f:
63
+ code = f.read()
64
+
65
+ tree = ast.parse(code)
66
+
67
+
68
+ pipeline = TransformerPipeline(
69
+ transformers=[
70
+ ConstsTransformer,
71
+ BasicFunctions,
72
+ BasicAttributes,
73
+ LCTransformer,
74
+ ConditionSimplifier,
75
+ NameReplacer,
76
+ DeadCodeRemover,
77
+ ],
78
+ iterations=args.iterations,
79
+ max_iterations=args.max_iterations,
80
+ max_depth=args.max_depth,
81
+ max_allocation=args.max_allocation,
82
+ )
83
+
84
+ result = pipeline.visit(tree)
85
+
86
+ new_code = ast.unparse(result)
87
+
88
+ res = black.format_str(new_code, mode=black.Mode())
89
+
90
+ with open(args.output, "w", encoding="utf-8") as f:
91
+ f.write(res)
92
+
93
+ LOGGER.debug(f"Output written to {args.output}")
94
+
95
+ if args.stdout:
96
+ print(res)
97
+
98
+
99
+ if __name__ == "__main__":
100
+ main()
noctyra-0.1.0/make.bat ADDED
@@ -0,0 +1,21 @@
1
+ @echo off
2
+ if "%1"=="test" (
3
+ uv run pytest tests/
4
+ exit /b %ERRORLEVEL%
5
+ ) else if "%1"=="lint" (
6
+ uv run ruff check .
7
+ exit /b %ERRORLEVEL%
8
+ ) else if "%1"=="format" (
9
+ uv run ruff format .
10
+ uv run ruff check . --fix
11
+ exit /b %ERRORLEVEL%
12
+ ) else if "%1"=="typecheck" (
13
+ uv run mypy . --ignore-missing-imports
14
+ exit /b %ERRORLEVEL%
15
+ ) else (
16
+ echo Noctyra Development Commands (Windows)
17
+ echo make test - Run unit tests
18
+ echo make lint - Check code style with ruff
19
+ echo make format - Format code with ruff
20
+ echo make typecheck - Run mypy type checking
21
+ )