Skip to content
Merged
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
6 changes: 5 additions & 1 deletion CAVEATS.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,4 +430,8 @@ does not make backups of the existing files, so make sure to do that first.

To check for enum use regression you can add `--check`. This will change the return
code to the number of enums that require changing. A return code of zero indicates
that no enum changes are required.
that no enum changes are required. This check can only find and fix code that
is directly using enums on Qt class names. If you used `self.EnumName` or other
methods of accessing the enum objects the `--partial` check may find them. This is
unable to automatically fix your code but shows you possible code that you will
need to manually fix.
74 changes: 69 additions & 5 deletions Qt_convert_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import re
import sys
from pathlib import Path
from typing import Optional


def qt_for_binding(binding):
Expand All @@ -38,7 +39,7 @@ def qt_for_binding(binding):


class QtEnumConverter:
DEFAULT_IGNORED = ".tox,.eggs,build"
DEFAULT_IGNORED = ".tox,.eggs,build,dist"

def __init__(self, verbosity=0, ignored: str = DEFAULT_IGNORED):
self.ignored = ignored.split(",")
Expand All @@ -52,6 +53,7 @@ def __init__(self, verbosity=0, ignored: str = DEFAULT_IGNORED):
# The mapping of short to fully qualified enum names. Used to
# update python scripts.
self.enum_map: dict[str, str] = {}
self.partial_enum: Optional[re.Pattern] = None
# Module level view of all the enums also used to check for naming
# conflicts where the short name maps to multiple long names.
self.enum_module: dict[str, dict[str, list[str]]] = {}
Expand Down Expand Up @@ -148,7 +150,45 @@ def convert_enums_in_file(self, filepath: Path, root: Path, dry_run: bool) -> in
f.write(content)
return changes

def convert_all(self, directory: Path, dry_run: bool) -> int:
def find_partials(self, filepath: Path, root: Path) -> int:
"""Find and report potential partial Enum uses

We can't automatically replace them as this has a high likelihood of
false positives.
"""
if not self.enum_map:
self.enums_for_qt_py()

if not self.partial_enum:
enums = {v.split('.')[-1] for v in self.enum_map.values()}
self.partial_enum = re.compile(fr"\.({'|'.join(enums)})(?!\w)")

# Read the content
with filepath.open('r', encoding='utf-8', newline='\n', errors='replace') as f:
content = f.readlines()

changes = 0
relative = filepath.relative_to(root)
for ln, line in enumerate(content):
clean_line = line.rstrip()
matches = set()
for v in self.enum_map.values():
clean_line = clean_line.replace(v, "")

for match in self.partial_enum.findall(clean_line):
matches.add(match)
if matches:
if self.verbosity >= 0:
print(
f'File: "{relative}", line:{ln + 1}, for {", ".join(matches)}'
)
if self.verbosity >= 1:
print(f' {line.strip()}')
changes += 1

return changes

def convert_all(self, directory: Path, dry_run: bool, partial: bool = False) -> int:
"""Search and replace all enums."""
ignored = [directory / i for i in self.ignored]
changes = 0
Expand All @@ -174,7 +214,10 @@ def convert_all(self, directory: Path, dry_run: bool) -> int:
continue
if self.verbosity >= 2:
print(f"Checking: {filepath}")
changes += self.convert_enums_in_file(filepath, directory, dry_run)
if partial:
changes += self.find_partials(filepath, directory)
else:
changes += self.convert_enums_in_file(filepath, directory, dry_run)

return changes

Expand Down Expand Up @@ -249,13 +292,27 @@ def parse_args():
"have duplicate names on the same class causing ambiguity with short "
"names. dups requires PySide6 and the others require PySide2 installed.",
)
parser.add_argument(
"--partial",
action="store_true",
help="Report any potential enum uses that can not be automatically fixed. "
"This can happen if the code accesses enums from the instance instead of "
"directly from the class name.",
)
parser.add_argument(
'-v',
'--verbosity',
action='count',
default=0,
help="Increase the verbosity of the output.",
)
parser.add_argument(
'-q',
'--quiet',
action="count",
default=0,
help="Decrease the verbosity of the output.",
)
parser.add_argument(
'--check',
action='store_true',
Expand All @@ -282,6 +339,9 @@ def add_binding_info(data, qt):
if __name__ == "__main__":
args = parse_args()

# Merge the quiet and verbosity arguments
args.verbosity -= args.quiet

if args.show == "dups":
# Show duplicate enum short names found in PySide6
checker = DuplicateEnums()
Expand All @@ -303,10 +363,14 @@ def add_binding_info(data, qt):
print(json.dumps(mappings, indent=4, sort_keys=True))
else:
# Search .py files and update to fully qualified enum names
changes = mapper.convert_all(args.target, dry_run=not args.write)
changes = mapper.convert_all(
args.target, dry_run=not args.write, partial=args.partial
)

# Report the number of enum changes
if args.write:
if args.partial:
print(f"{changes} partial enums found.")
elif args.write:
print(f"{changes} enums changed.")
else:
print(f"{changes} enums require changes.")
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ universal=1
[flake8]
select = B, C, E, F, N, W, B9
extend-ignore =
B907,
E203,
E501,
E722,
Expand Down
Loading