210 lines
6.9 KiB
Python
210 lines
6.9 KiB
Python
|
#! /usr/bin/python3
|
||
|
#
|
||
|
# The original enum-converter.py may be found at:
|
||
|
# https://github.com/PackageKit/PackageKit/blob/b64ee9dfa707d5dd2b93c8eebe9930a55fcde108/lib/python/enum-convertor.py
|
||
|
#
|
||
|
# Copyright (C) 2008 - 2012 PackageKit Authors
|
||
|
#
|
||
|
# Licensed under the GNU General Public License Version 2.0
|
||
|
# This program is free software; you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published by
|
||
|
# the Free Software Foundation; either version 2 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# The following modifications have been made to the original
|
||
|
# script:
|
||
|
# * Print the time of conversion
|
||
|
# * Extract and print the original license of the source
|
||
|
# * Enumerations are extracted from the header file to preserve
|
||
|
# order
|
||
|
# * Use Python "Flag" Enumerations
|
||
|
# * Extract comments and include them as docstrings
|
||
|
# * Introduce a string constant validation mode. This extracts
|
||
|
# strings from pk-enum.c, then compares to the calculated
|
||
|
# strings from pk-enum.h.
|
||
|
#
|
||
|
# Copyright (C) 2022 Eric Callahan <arksine.code@gmail.com>
|
||
|
#
|
||
|
# Usage:
|
||
|
# pk-enum-converter.py pk_enum.h > enums.py
|
||
|
#
|
||
|
# Enum String Validation Mode:
|
||
|
# pk-enum-converter.py pk_enum.h pk_enum.c
|
||
|
#
|
||
|
# The pk_enum source files, pk-enum.c and pk-enum.h, can be found in the
|
||
|
# PackageKit GitHub repo:
|
||
|
# https://github.com/PackageKit/PackageKit/blob/main/lib/packagekit-glib2/pk-enum.c
|
||
|
# https://github.com/PackageKit/PackageKit/blob/main/lib/packagekit-glib2/pk-enum.h
|
||
|
#
|
||
|
from __future__ import print_function
|
||
|
|
||
|
from re import compile, DOTALL, MULTILINE
|
||
|
import time
|
||
|
import sys
|
||
|
import pathlib
|
||
|
import textwrap
|
||
|
|
||
|
HEADER = \
|
||
|
'''
|
||
|
# This file was autogenerated from %s by pk-enum-converter.py
|
||
|
# on %s UTC
|
||
|
#
|
||
|
# License for original source:
|
||
|
#
|
||
|
%s
|
||
|
|
||
|
from __future__ import annotations
|
||
|
import sys
|
||
|
from enum import Flag, auto
|
||
|
|
||
|
class PkFlag(Flag):
|
||
|
@classmethod
|
||
|
def from_pkstring(cls, pkstring: str):
|
||
|
for name, member in cls.__members__.items():
|
||
|
if member.pkstring == pkstring:
|
||
|
return cls(member.value)
|
||
|
# Return "unknown" flag
|
||
|
return cls(1)
|
||
|
|
||
|
@classmethod
|
||
|
def from_index(cls, index: int):
|
||
|
return cls(1 << index)
|
||
|
|
||
|
@property
|
||
|
def pkstring(self) -> str:
|
||
|
if self.name is None:
|
||
|
return " | ".join([f.pkstring for f in self])
|
||
|
return self.name.lower().replace("_", "-")
|
||
|
|
||
|
@property
|
||
|
def desc(self) -> str:
|
||
|
if self.name is None:
|
||
|
return ", ".join([f.desc for f in self])
|
||
|
description = self.name.lower().replace("_", " ")
|
||
|
return description.capitalize()
|
||
|
|
||
|
@property
|
||
|
def index(self) -> int:
|
||
|
return self.value.bit_length() - 1
|
||
|
|
||
|
if sys.version_info < (3, 11):
|
||
|
def __iter__(self):
|
||
|
for i in range(self._value_.bit_length()):
|
||
|
val = 1 << i
|
||
|
if val & self._value_ == val:
|
||
|
yield self.__class__(val)
|
||
|
''' # noqa: E122
|
||
|
|
||
|
FILTER_PKSTRING = \
|
||
|
''' @property
|
||
|
def pkstring(self) -> str:
|
||
|
pks = self.name
|
||
|
if pks is None:
|
||
|
return " | ".join([f.pkstring for f in self])
|
||
|
if pks in ["DEVELOPMENT", "NOT_DEVELOPMENT"]:
|
||
|
pks = pks[:-6]
|
||
|
if pks[:4] == "NOT_":
|
||
|
pks = "~" + pks[4:]
|
||
|
return pks.lower().replace("_", "-")
|
||
|
''' # noqa: E122
|
||
|
|
||
|
ERROR_PROPS = \
|
||
|
''' @property
|
||
|
def pkstring(self) -> str:
|
||
|
if self == Error.UPDATE_FAILED_DUE_TO_RUNNING_PROCESS:
|
||
|
return "failed-due-to-running-process"
|
||
|
return super().pkstring
|
||
|
''' # noqa: E122
|
||
|
|
||
|
ALIASES = {
|
||
|
"Error.OOM": "OUT_OF_MEMORY"
|
||
|
}
|
||
|
|
||
|
header_enum = compile(r"/\*\*\n(.+?)@PK_[A-Z_]+_ENUM_LAST:\s+\*\s+(.+?)"
|
||
|
r"\s+\*\*/\s+typedef enum {(.+?)} Pk(.+?)Enum",
|
||
|
DOTALL | MULTILINE)
|
||
|
header_value = compile(r"(PK_[A-Z_]+_ENUM)_([A-Z0-9_]+)")
|
||
|
header_desc = compile(r"@PK_[A-Z_]+_ENUM_([A-Z_]+):(.*)")
|
||
|
license = compile(r"(Copyright.+?)\*/", DOTALL | MULTILINE)
|
||
|
enum_h_name = sys.argv[1]
|
||
|
header = pathlib.Path(enum_h_name).read_text()
|
||
|
|
||
|
# Get License
|
||
|
lic_match = license.search(header)
|
||
|
assert lic_match is not None
|
||
|
lic_parts = lic_match.group(1).split("\n")
|
||
|
lic = "\n".join([("# " + p.lstrip("* ")).rstrip(" ") for p in lic_parts])
|
||
|
|
||
|
|
||
|
if len(sys.argv) > 2:
|
||
|
# Validation Mode, extract strings from the source file, compare to
|
||
|
# those calculated from the enums in the header file
|
||
|
enum_to_string = {}
|
||
|
enum = compile(r"static const PkEnumMatch enum_([^\]]+)\[\] = {(.*?)};",
|
||
|
DOTALL | MULTILINE)
|
||
|
value = compile(r"(PK_[A-Z_]+_ENUM_[A-Z0-9_]+),\s+\"([^\"]+)\"")
|
||
|
enum_c_name = sys.argv[2]
|
||
|
inp = pathlib.Path(enum_c_name).read_text()
|
||
|
for (name, data) in enum.findall(inp):
|
||
|
for (enum_name, string) in value.findall(data):
|
||
|
enum_to_string[enum_name] = string
|
||
|
for (desc_data, comments, data, name) in header_enum.findall(header):
|
||
|
for (prefix, short_name) in header_value.findall(data):
|
||
|
if short_name == "LAST":
|
||
|
continue
|
||
|
# Validation Mode
|
||
|
enum_name = f"{prefix}_{short_name}"
|
||
|
string = enum_to_string[enum_name]
|
||
|
calc_string = short_name.lower().replace("_", "-")
|
||
|
if calc_string[:4] == "not-" and name == "Filter":
|
||
|
calc_string = "~" + calc_string[4:]
|
||
|
if calc_string != string:
|
||
|
print(
|
||
|
f"Calculated String Mismatch: {name}.{short_name}\n"
|
||
|
f"Calculated: {calc_string}\n"
|
||
|
f"Extracted: {string}\n")
|
||
|
exit(0)
|
||
|
|
||
|
print(HEADER % (enum_h_name, time.asctime(time.gmtime()), lic))
|
||
|
# Use the header file for correct enum ordering
|
||
|
for (desc_data, comments, data, name) in header_enum.findall(header):
|
||
|
|
||
|
print(f"\nclass {name}(PkFlag):")
|
||
|
# Print Docstring
|
||
|
print(' """')
|
||
|
comments = [(" " * 4 + c.lstrip("* ")).rstrip(" ")
|
||
|
for c in comments.splitlines()]
|
||
|
for comment in comments:
|
||
|
comment = comment.expandtabs(4)
|
||
|
if len(comment) > 79:
|
||
|
comment = "\n".join(textwrap.wrap(
|
||
|
comment, 79, subsequent_indent=" ",
|
||
|
tabsize=4))
|
||
|
print(comment)
|
||
|
print("")
|
||
|
for (item, desc) in header_desc.findall(desc_data):
|
||
|
line = f" * {name}.{item}: {desc}".rstrip()
|
||
|
if len(line) > 79:
|
||
|
print(f" * {name}.{item}:")
|
||
|
print(f" {desc}")
|
||
|
else:
|
||
|
print(line)
|
||
|
print(' """')
|
||
|
if name == "Filter":
|
||
|
print(FILTER_PKSTRING)
|
||
|
elif name == "Error":
|
||
|
print(ERROR_PROPS)
|
||
|
aliases = []
|
||
|
for (prefix, short_name) in header_value.findall(data):
|
||
|
if short_name == "LAST":
|
||
|
continue
|
||
|
long_name = f"{name}.{short_name}"
|
||
|
if long_name in ALIASES:
|
||
|
alias = ALIASES[long_name]
|
||
|
aliases.append((short_name, alias))
|
||
|
short_name = alias
|
||
|
# Print Enums
|
||
|
print(f" {short_name} = auto()")
|
||
|
for name, alias in aliases:
|
||
|
print(f" {name} = {alias}")
|