Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/out_folder_cli/a..translator_cli
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
asdasdasdasdasdasdaasdasd
1 change: 1 addition & 0 deletions python/out_folder_cli/abc/b..translator_cli
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
asafjkbnia 99999123idnjf
18 changes: 18 additions & 0 deletions python/retranslator/Translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from .sub_rule import SubRule
from .stepped_translator import SteppedTranslator
from .translator_extensions import TranslatorExtensions


class Translator:
Expand Down Expand Up @@ -37,3 +38,20 @@ def translate(
while stpd_translator.next():
pass
return stpd_translator.text

def get_steps(self, source_text: str) -> List[str]:
"""Gets the transformation steps for debugging.

:param source_text: The source text to transform
:return: List of transformation steps
"""
return TranslatorExtensions.get_steps(self, source_text)

def write_steps_to_files(self, source_text: str, target_path: str, skip_files_with_no_changes: bool = True):
"""Writes transformation steps to files for debugging.

:param source_text: The source text to transform
:param target_path: The target file path
:param skip_files_with_no_changes: Skip writing files when step produces no changes
"""
return TranslatorExtensions.write_steps_to_files(self, source_text, target_path, skip_files_with_no_changes)
9 changes: 6 additions & 3 deletions python/retranslator/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# -*- coding: utf-8 -*-
from .sub_rule import SubRule
from .stepped_translator import SteppedTranslator
from .translator import Translator
from .Translator import Translator
from .file_translator import FileTranslator
from .translator_cli import TranslatorCLI
from .logging_file_translator import LoggingFileTranslator
from .translator_extensions import TranslatorExtensions

__version__ = "0.2.2"
__version__ = "0.3.0"
__copyright__ = "2022"
__authors__ = ["Ethosa", "Konard"]
__all__ = [
'SubRule', 'SteppedTranslator', 'Translator',
'FileTranslator', 'TranslatorCLI'
'FileTranslator', 'TranslatorCLI', 'LoggingFileTranslator',
'TranslatorExtensions'
]
2 changes: 1 addition & 1 deletion python/retranslator/file_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import NoReturn, Optional, List
from os import listdir, path, getcwd, mkdir

from .translator import Translator
from .Translator import Translator


class FileTranslator(Translator):
Expand Down
26 changes: 26 additions & 0 deletions python/retranslator/logging_file_translator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from typing import NoReturn, Optional
from .file_translator import FileTranslator
from .translator_extensions import TranslatorExtensions


class LoggingFileTranslator(FileTranslator):
"""File transformer with logging capabilities, similar to C# LoggingFileTransformer"""

def translate_file(
self,
src_file: str,
target_file: str
) -> NoReturn:
"""Translates source file and writes debugging steps
"""
# First do the normal file translation
super().translate_file(src_file, target_file)

# Then write debugging steps
with open(src_file, 'r', encoding='utf-8') as f:
source_text = f.read()

TranslatorExtensions.write_steps_to_files(
self._translator, source_text, target_file, skip_files_with_no_changes=True
)
8 changes: 4 additions & 4 deletions python/retranslator/stepped_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ def next(
return False

rule = self.rules[self.current]
replace = -1
replace_count = 0

while search(rule.match, self.text) and rule.max_repeat > replace:
self.text = sub(rule.match, rule.sub, self.text)
replace += 1
while rule.match.search(self.text) and (rule.max_repeat == 0 or replace_count < rule.max_repeat):
self.text = rule.match.sub(rule.sub, self.text)
replace_count += 1

self.current += 1
return True
93 changes: 70 additions & 23 deletions python/retranslator/sub_rule.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,91 @@
# -*- coding: utf-8 -*-
from typing import NoReturn, Optional
from typing import NoReturn, Optional, Union
import sys

import regex as re
from regex import Pattern, MULTILINE


class SubRule:
options = MULTILINE # default regex options
match: Pattern = r'' # match pattern
sub: Pattern = r'' # substitution pattern
path: Optional[Pattern] = None # path pattern
max_repeat: int = 0 # maximum repeat count

"""Substitution rule class, similar to C# SubstitutionRule"""

DEFAULT_REGEX_OPTIONS = MULTILINE # default regex options
DEFAULT_TIMEOUT = 300 # 5 minutes timeout (similar to C# default)

def __init__(
self,
match: Pattern,
sub: Pattern,
path: Optional[Pattern] = None,
match: Union[str, Pattern],
sub: str,
path: Optional[Union[str, Pattern]] = None,
max_repeat: int = 0,
regex_options: int = options
regex_options: int = DEFAULT_REGEX_OPTIONS,
match_timeout: Optional[float] = None
):
"""Initializes Substitution rule.

:param match: match pattern
:param match: match pattern (string or compiled Pattern)
:param sub: substitution pattern
:param path: path pattern
:param max_repeat: max match repeat
:regex_options: regular expression options. by default is Multiline.
:param path: path pattern (string or compiled Pattern)
:param max_repeat: max match repeat (0 means unlimited)
:param regex_options: regular expression options. by default is Multiline | Compiled.
:param match_timeout: timeout for regex matching in seconds (None for default)
"""
self.match = match
# Compile match pattern if needed
if isinstance(match, str):
self.match = re.compile(match, regex_options)
else:
self.match = match

self.sub = sub
self.path = path

# Compile path pattern if needed
if path is not None:
if isinstance(path, str):
self.path = re.compile(path, regex_options)
else:
self.path = path
else:
self.path = None

self.max_repeat = max_repeat
self.options = regex_options
self.match_timeout = match_timeout or self.DEFAULT_TIMEOUT

def override_match_pattern_options(self, options: int, match_timeout: Optional[float] = None):
"""Override match pattern options, similar to C# OverrideMatchPatternOptions"""
if isinstance(self.match, Pattern):
pattern_str = self.match.pattern
else:
pattern_str = str(self.match)

timeout = match_timeout or self.match_timeout
self.match = re.compile(pattern_str, options)
self.options = options
self.match_timeout = timeout

def override_path_pattern_options(self, options: int, match_timeout: Optional[float] = None):
"""Override path pattern options, similar to C# OverridePathPatternOptions"""
if self.path is not None:
if isinstance(self.path, Pattern):
pattern_str = self.path.pattern
else:
pattern_str = str(self.path)

timeout = match_timeout or self.match_timeout
self.path = re.compile(pattern_str, options)

def __str__(
self
) -> str:
result = f'"{self.match}" -> "{self.sub}"'
def __str__(self) -> str:
"""String representation similar to C# ToString method"""
result = f'"{self.match.pattern if hasattr(self.match, "pattern") else self.match}" -> "{self.sub}"'

if self.path:
result = f'{result} on files "{self.path}"'
path_str = self.path.pattern if hasattr(self.path, "pattern") else self.path
result = f'{result} on files "{path_str}"'

if self.max_repeat > 0:
result = f'{result} repeated {self.max_repeat} times'
if self.max_repeat >= sys.maxsize:
result = f'{result} repeated forever'
else:
result = f'{result} repeated up to {self.max_repeat} times'

return result
2 changes: 1 addition & 1 deletion python/retranslator/translator_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from regex import Pattern

from .translator import Translator
from .Translator import Translator


class TranslatorCLI:
Expand Down
93 changes: 93 additions & 0 deletions python/retranslator/translator_extensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
from typing import List, Optional
import os
import glob
from .stepped_translator import SteppedTranslator


class TranslatorExtensions:
"""Extension methods for Translator class, similar to C# ITextTransformerExtensions"""

@staticmethod
def get_steps(translator, source_text: str) -> List[str]:
"""Gets the transformation steps, similar to C# GetSteps method

:param translator: The translator instance
:param source_text: The source text to transform
:return: List of transformation steps
"""
if not translator or not translator.rules:
return []

steps = []
stepped_translator = SteppedTranslator(translator.rules, source_text, 0)
while stepped_translator.next():
steps.append(stepped_translator.text)

return steps

@staticmethod
def write_steps_to_files(translator, source_text: str, target_path: str, skip_files_with_no_changes: bool = True):
"""Writes transformation steps to files for debugging, similar to C# WriteStepsToFiles method

:param translator: The translator instance
:param source_text: The source text to transform
:param target_path: The target file path
:param skip_files_with_no_changes: Skip writing files when step produces no changes
"""
if not translator or not translator.rules:
return

# Parse target path
directory = os.path.dirname(target_path) or '.'
filename = os.path.basename(target_path)
name, ext = os.path.splitext(filename)

# Delete all previous step files
TranslatorExtensions._delete_all_steps(directory, name, ext)

last_text = ""
stepped_translator = SteppedTranslator(translator.rules, source_text, 0)

while stepped_translator.next():
new_text = stepped_translator.text
TranslatorExtensions._write_step(
translator, directory, name, ext,
stepped_translator.current - 1, # Adjust for 0-based indexing
last_text, new_text, skip_files_with_no_changes
)
last_text = new_text

@staticmethod
def _delete_all_steps(directory: str, target_filename: str, target_extension: str):
"""Delete all step files from previous runs"""
# Delete rule files
rule_pattern = os.path.join(directory, f"{target_filename}.*.rule.txt")
for file in glob.glob(rule_pattern):
os.remove(file)

# Delete step files
step_pattern = os.path.join(directory, f"{target_filename}.*{target_extension}")
for file in glob.glob(step_pattern):
# Only delete files that match the step pattern (have number in them)
basename = os.path.basename(file)
if '.' in basename and basename.split('.')[-2].isdigit():
os.remove(file)

@staticmethod
def _write_step(transformer, directory: str, target_filename: str, target_extension: str,
current_step: int, last_text: str, new_text: str, skip_files_with_no_changes: bool):
"""Write a single transformation step to files"""
if skip_files_with_no_changes and last_text == new_text:
return

# Write the transformed text
step_file = os.path.join(directory, f"{target_filename}.{current_step}{target_extension}")
with open(step_file, 'w', encoding='utf-8') as f:
f.write(new_text)

# Write the rule used for this step
rule_string = str(transformer.rules[current_step])
rule_file = os.path.join(directory, f"{target_filename}.{current_step}.rule.txt")
with open(rule_file, 'w', encoding='utf-8') as f:
f.write(rule_string)
13 changes: 7 additions & 6 deletions python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,33 @@

setuptools.setup(
name="retranslator",
version="0.2.2",
version="0.3.0",
author="Ethosa",
author_email="[email protected]",
description="retranslator",
description="retranslator - Regular expressions transformer with all C# features",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/linksplatform/RegularExpressions.Transformer/tree/master/python",
packages=setuptools.find_packages(),
license="LGPLv3",
keywords="csharp cpp cs2cpp platform ethosa konard retranslator",
keywords="csharp cpp cs2cpp platform ethosa konard retranslator regex transformation",
classifiers=[
"Development Status :: 4 - Beta",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Operating System :: OS Independent",
],
project_urls={
"Github": "https://github.com/linksplatform/RegularExpressions.Transformer/tree/master/python",
"Documentation": "https://github.com/linksplatform/RegularExpressions.Transformer/tree/master/python",
},
python_requires=">=3",
python_requires=">=3.6",
install_requires=["regex"]
)
Loading
Loading