Improve make_repo.py
This cleans up make_repo.py a bit, using file extensions to determine file type. This also loosens the testing repo generation, as the existing test required matching a predifed expected output which had to be updated on every change (essentially making it a moot test, as the reference output was obtained from the functions output). The new test just checks if the output has the same number of packages as the input dir has addons. Tips on how best to test these sorts of "higher level" functions (if at all) would be welcome :)
This commit is contained in:
145
make_repo.py
145
make_repo.py
@@ -54,86 +54,120 @@ def extract_blinfo(item: Path) -> dict:
|
||||
"""
|
||||
|
||||
blinfo = None
|
||||
addon_name = item.name
|
||||
|
||||
if not item.exists():
|
||||
raise FileNotFoundError("Cannot extract blinfo from '%s'; no such file or directory" % item)
|
||||
|
||||
if item.is_dir():
|
||||
fname = item / '__init__.py'
|
||||
|
||||
try:
|
||||
with fname.open("r") as f:
|
||||
blinfo = parse_blinfo(f.read())
|
||||
f = (item / '__init__.py').open("r")
|
||||
except FileNotFoundError as err:
|
||||
# directory with no __init__.py: not an addon
|
||||
raise BadAddon("Directory '%s' doesn't contain __init__.py; not a python package" % item) from err
|
||||
with f:
|
||||
blinfo = parse_blinfo(f.read())
|
||||
|
||||
elif item.is_file():
|
||||
try:
|
||||
with zipfile.ZipFile(str(item), 'r') as z:
|
||||
if len(z.namelist()) == 1:
|
||||
# zipfile with one item: just read that item
|
||||
blinfo = parse_blinfo(z.read(z.namelist()[0]))
|
||||
else:
|
||||
# zipfile with multiple items: try all __init__.py files
|
||||
for fname in z.namelist():
|
||||
# TODO: zips with multiple bl_infos might be a problem,
|
||||
# not sure how such cases should be handled (if at all)
|
||||
# for now we just break after the first one
|
||||
if fname.endswith('__init__.py'):
|
||||
try:
|
||||
blinfo = parse_blinfo(z.read(fname))
|
||||
break
|
||||
except BadAddon:
|
||||
continue
|
||||
if blinfo is None:
|
||||
raise BadAddon("Zipfile '%s' doesn't contain a readable bl_info dict" % item)
|
||||
|
||||
except zipfile.BadZipFile:
|
||||
# If it's not a valid zip, assume file is just a normal file
|
||||
ext = item.suffix.lower()
|
||||
if ext == '.zip':
|
||||
try:
|
||||
with item.open() as f:
|
||||
blinfo = parse_blinfo(f.read())
|
||||
except: #HACK
|
||||
# If it's not a zip and its not parse-able python, then it's not an addon
|
||||
raise BadAddon("File '%s' doesn't appear to be an addon" % item)
|
||||
with zipfile.ZipFile(str(item), 'r') as z:
|
||||
if len(z.namelist()) == 1:
|
||||
# zipfile with one item: just read that item
|
||||
blinfo = parse_blinfo(z.read(z.namelist()[0]))
|
||||
else:
|
||||
# zipfile with multiple items: try all __init__.py files
|
||||
for fname in z.namelist():
|
||||
# TODO: zips with multiple bl_infos might be a problem,
|
||||
# not sure how such cases should be handled (if at all)
|
||||
# for now we just break after the first one
|
||||
if fname.endswith('__init__.py'):
|
||||
try:
|
||||
blinfo = parse_blinfo(z.read(fname))
|
||||
break
|
||||
except BadAddon:
|
||||
continue
|
||||
if blinfo is None:
|
||||
raise BadAddon("Zipfile '%s' doesn't contain a readable bl_info dict" % item)
|
||||
except zipfile.BadZipFile as e:
|
||||
raise BadAddon("Bad zipfile '%s'" % item) from e
|
||||
|
||||
elif ext == '.py':
|
||||
with item.open() as f:
|
||||
blinfo = parse_blinfo(f.read())
|
||||
|
||||
else:
|
||||
raise BadAddon("File '%s' doesn't have a .zip or .py extension; not an addon" % item)
|
||||
|
||||
# This should not happen
|
||||
if blinfo == None:
|
||||
raise RuntimeError("Could not read addon '%s'" % addon_name)
|
||||
if blinfo is None:
|
||||
raise RuntimeError("Could not read addon '%s'" % item.name)
|
||||
|
||||
return blinfo
|
||||
|
||||
|
||||
class Package:
|
||||
def __init__(self, path: Path, bl_info: dict, baseurl=None):
|
||||
self.bl_info = bl_info
|
||||
self.path = path
|
||||
self.url = None
|
||||
|
||||
def make_repo(path: Path):
|
||||
def dict(self) -> dict:
|
||||
return {
|
||||
'bl_info': self.bl_info,
|
||||
'url': self.url,
|
||||
}
|
||||
|
||||
class Repository:
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
self.url = None
|
||||
self.packages = []
|
||||
|
||||
def add_package(self, pkg: Package):
|
||||
# if pkg.url is None:
|
||||
# pkg.url =
|
||||
self.packages.append(pkg)
|
||||
|
||||
def dict(self) -> dict:
|
||||
return {
|
||||
'name': self.name,
|
||||
'packages': [p.dict() for p in self.packages],
|
||||
'url': self.url,
|
||||
}
|
||||
|
||||
def dump(self, path: Path):
|
||||
with (path / 'repo.json').open('w', encoding='utf-8') as repo_file:
|
||||
json.dump(self.dict(), repo_file, indent=4, sort_keys=True)
|
||||
log.info("repo.json written to %s" % path)
|
||||
|
||||
|
||||
def json(self) -> str:
|
||||
return json.dumps(self.__dict__)
|
||||
|
||||
|
||||
def make_repo(path: Path, name: str) -> Repository:
|
||||
"""Make repo.json for files in directory 'path'"""
|
||||
|
||||
repo_data = {}
|
||||
package_data = []
|
||||
repo = Repository(name)
|
||||
|
||||
if not path.is_dir():
|
||||
raise FileNotFoundError(path)
|
||||
|
||||
for addon, bl_info in iter_addons(path):
|
||||
package_datum = {}
|
||||
|
||||
# Check if we have all bl_info fields we want
|
||||
if not REQUIRED_KEYS.issubset(set(bl_info)):
|
||||
log.warning(
|
||||
"Required key(s) '{}' not found in bl_info of '{}'".format(
|
||||
"', '".join(REQUIRED_KEYS.difference(set(bl_info))), addon)
|
||||
)
|
||||
|
||||
package_datum['bl_info'] = bl_info
|
||||
package_datum['type'] = 'addon'
|
||||
package_data.append(package_datum)
|
||||
|
||||
repo_data['packages'] = package_data
|
||||
package = Package(addon, bl_info)
|
||||
repo.add_package(package)
|
||||
|
||||
log.info("Repository generation successful")
|
||||
cwd = Path.cwd()
|
||||
dump_repo(cwd, repo_data)
|
||||
log.info("repo.json written to %s" % cwd)
|
||||
return repo
|
||||
|
||||
|
||||
def main():
|
||||
@@ -143,18 +177,25 @@ def main():
|
||||
help="Increase verbosity (can be used multiple times)",
|
||||
action="count",
|
||||
default=0)
|
||||
parser.add_argument('-n', '--name',
|
||||
help="Name of repo (defaults to basename of 'path')")
|
||||
parser.add_argument('-o', '--output',
|
||||
help="Directory in which to write repo.json file",
|
||||
type=Path,
|
||||
default=Path.cwd())
|
||||
parser.add_argument('path',
|
||||
type=Path,
|
||||
nargs='?',
|
||||
default=Path.cwd(),
|
||||
help="Path to addon directory")
|
||||
|
||||
args = parser.parse_args()
|
||||
log.level = args.verbose
|
||||
logging.basicConfig(level=logging.INFO,
|
||||
format='%(levelname)8s: %(message)s')
|
||||
if args.name is None:
|
||||
args.name = args.path.name
|
||||
|
||||
make_repo(args.path)
|
||||
logging.basicConfig(format='%(levelname)8s: %(message)s', level=logging.INFO)
|
||||
log.level += args.verbose
|
||||
|
||||
repo = make_repo(args.path, args.name)
|
||||
repo.dump(args.output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
Reference in New Issue
Block a user