Cleanup: Changes suggested by @sybren
This commit is contained in:
126
make_repo.py
126
make_repo.py
@@ -20,10 +20,9 @@ def iter_addons(path: Path) -> (Path, dict):
|
||||
"""
|
||||
Generator, yields (path, bl_info) of blender addons in `path`.
|
||||
"""
|
||||
pass
|
||||
for item in path.iterdir():
|
||||
try:
|
||||
yield(item, extract_blinfo(item))
|
||||
yield (item, extract_blinfo(item))
|
||||
except BadAddon as err:
|
||||
log.debug("Skipping '{}': {}".format(item.name, err))
|
||||
|
||||
@@ -48,104 +47,122 @@ def parse_blinfo(source: str) -> dict:
|
||||
raise BadAddon('No bl_info found')
|
||||
|
||||
|
||||
def blinfo_from_zip(item: Path) -> dict:
|
||||
try:
|
||||
with zipfile.ZipFile(str(item), 'r') as z:
|
||||
if len(z.namelist()) == 1:
|
||||
# zipfile with one item: just read that item
|
||||
return 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:
|
||||
return parse_blinfo(z.read(fname))
|
||||
except BadAddon:
|
||||
continue
|
||||
|
||||
raise BadAddon("Zipfile '%s' doesn't contain a readable bl_info dict" % item)
|
||||
|
||||
except zipfile.BadZipFile as err:
|
||||
raise BadAddon("Bad zipfile '%s'" % item) from err
|
||||
|
||||
|
||||
def blinfo_from_py(item: Path) -> dict:
|
||||
with item.open() as f:
|
||||
return parse_blinfo(f.read())
|
||||
|
||||
def blinfo_from_dir(item: Path) -> dict:
|
||||
try:
|
||||
f = (item / '__init__.py').open("r")
|
||||
except FileNotFoundError as err:
|
||||
raise BadAddon("Directory '%s' doesn't contain __init__.py; not a python package" % item) from err
|
||||
with f:
|
||||
return parse_blinfo(f.read())
|
||||
|
||||
def extract_blinfo(item: Path) -> dict:
|
||||
"""
|
||||
Extract bl_info dict from addon at path (can be single file, python package, or zip)
|
||||
"""
|
||||
|
||||
blinfo = None
|
||||
|
||||
if not item.exists():
|
||||
raise FileNotFoundError("Cannot extract blinfo from '%s'; no such file or directory" % item)
|
||||
|
||||
if item.is_dir():
|
||||
|
||||
try:
|
||||
f = (item / '__init__.py').open("r")
|
||||
except FileNotFoundError as err:
|
||||
raise BadAddon("Directory '%s' doesn't contain __init__.py; not a python package" % item) from err
|
||||
with f:
|
||||
blinfo = parse_blinfo(f.read())
|
||||
return blinfo_from_dir(item)
|
||||
|
||||
elif item.is_file():
|
||||
if item.is_file():
|
||||
ext = item.suffix.lower()
|
||||
|
||||
if ext == '.zip':
|
||||
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 as e:
|
||||
raise BadAddon("Bad zipfile '%s'" % item) from e
|
||||
|
||||
return blinfo_from_zip(item)
|
||||
elif ext == '.py':
|
||||
with item.open() as f:
|
||||
blinfo = parse_blinfo(f.read())
|
||||
|
||||
return blinfo_from_py(item)
|
||||
else:
|
||||
raise BadAddon("File '%s' doesn't have a .zip or .py extension; not an addon" % item)
|
||||
|
||||
# This should not happen
|
||||
if blinfo is None:
|
||||
raise RuntimeError("Could not read addon '%s'" % item.name)
|
||||
|
||||
return blinfo
|
||||
|
||||
raise RuntimeError("Could not read addon '%s'" % item.name)
|
||||
|
||||
class Package:
|
||||
def __init__(self, path: Path, bl_info: dict, baseurl=None):
|
||||
"""
|
||||
Stores package path and metadata
|
||||
"""
|
||||
|
||||
def __init__(self, path: Path, bl_info: dict):
|
||||
self.bl_info = bl_info
|
||||
self.path = path
|
||||
self.url = None
|
||||
|
||||
def dict(self) -> dict:
|
||||
def to_dict(self) -> dict:
|
||||
"""
|
||||
Return a dict representation of the package
|
||||
"""
|
||||
return {
|
||||
'bl_info': self.bl_info,
|
||||
'url': self.url,
|
||||
}
|
||||
|
||||
class Repository:
|
||||
"""
|
||||
Stores repository metadata (including a list of packages)
|
||||
"""
|
||||
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
self.url = None
|
||||
self.packages = []
|
||||
|
||||
def add_package(self, pkg: Package):
|
||||
"""
|
||||
Add a package to the repository
|
||||
"""
|
||||
# if pkg.url is None:
|
||||
# pkg.url =
|
||||
self.packages.append(pkg)
|
||||
|
||||
def dict(self) -> dict:
|
||||
def to_dict(self) -> dict:
|
||||
"""
|
||||
Return a dict representation of the repository
|
||||
"""
|
||||
return {
|
||||
'name': self.name,
|
||||
'packages': [p.dict() for p in self.packages],
|
||||
'packages': [p.to_dict() for p in self.packages],
|
||||
'url': self.url,
|
||||
}
|
||||
|
||||
def dump(self, path: Path):
|
||||
"""
|
||||
Dump repository as a repo.json file in 'path'
|
||||
"""
|
||||
with (path / 'repo.json').open('w', encoding='utf-8') as repo_file:
|
||||
json.dump(self.dict(), repo_file, indent=4, sort_keys=True)
|
||||
json.dump(self.to_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'"""
|
||||
|
||||
@@ -188,12 +205,15 @@ def main():
|
||||
help="Path to addon directory")
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.name is None:
|
||||
args.name = args.path.name
|
||||
|
||||
logging.basicConfig(format='%(levelname)8s: %(message)s', level=logging.INFO)
|
||||
log.level += args.verbose
|
||||
|
||||
if args.name is None:
|
||||
args.name = args.path.name
|
||||
|
||||
log.debug("Repository name: '%s'" % args.name)
|
||||
|
||||
repo = make_repo(args.path, args.name)
|
||||
repo.dump(args.output)
|
||||
|
||||
|
@@ -10,7 +10,7 @@ import make_repo
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='%(levelname)8s: %(message)s')
|
||||
|
||||
class test_make_repo(unittest.TestCase):
|
||||
class TestRepoGeneration(unittest.TestCase):
|
||||
|
||||
helper_path = Path('tests', 'test_helpers')
|
||||
addon_path = helper_path / 'addons'
|
||||
@@ -20,21 +20,6 @@ class test_make_repo(unittest.TestCase):
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
make_repo.extract_blinfo(self.addon_path / test_file)
|
||||
|
||||
# def test_make_repo_valid(self):
|
||||
# reference_repo = make_repo.make_repo(self.helper_path / 'addons', "test repo")
|
||||
# repojson = Path.cwd() / 'repo.json'
|
||||
# reference_repojson = self.helper_path / 'repo.json'
|
||||
#
|
||||
# try:
|
||||
# with repojson.open('r') as repolist_f:
|
||||
# with reference_repojson.open('r') as ref_repolist_f:
|
||||
# repolist = json.loads(repolist_f.read())
|
||||
# ref_repolist = json.loads(ref_repolist_f.read())
|
||||
# self.assertEqual(repolist, ref_repolist)
|
||||
# finally:
|
||||
# # repojson.unlink()
|
||||
# pass
|
||||
|
||||
def test_package_quantity(self):
|
||||
repo = make_repo.make_repo(self.addon_path, "name of the repo")
|
||||
acceptible_addons = [
|
||||
@@ -49,12 +34,12 @@ class test_make_repo(unittest.TestCase):
|
||||
|
||||
# addons which should contain bl_infos
|
||||
yes_blinfo = [
|
||||
f for f in test_make_repo.addon_path.iterdir()
|
||||
f for f in TestRepoGeneration.addon_path.iterdir()
|
||||
if not f.match('*nonaddon*') and not f.match('*invalid_addon*')
|
||||
]
|
||||
# addons which should throw BadAddon because they have no blinfo
|
||||
no_blinfo = [
|
||||
f for f in test_make_repo.addon_path.iterdir()
|
||||
f for f in TestRepoGeneration.addon_path.iterdir()
|
||||
if f.match('*nonaddon*')
|
||||
]
|
||||
|
||||
@@ -81,6 +66,6 @@ def add_generated_tests(test_generator, params, destclass):
|
||||
test_func = test_generator(param)
|
||||
setattr(destclass, 'test_{}'.format(param), test_func)
|
||||
|
||||
add_generated_tests(generate_good_blinfo_test, yes_blinfo, test_make_repo)
|
||||
add_generated_tests(generate_bad_blinfo_test, no_blinfo, test_make_repo)
|
||||
add_generated_tests(generate_good_blinfo_test, yes_blinfo, TestRepoGeneration)
|
||||
add_generated_tests(generate_bad_blinfo_test, no_blinfo, TestRepoGeneration)
|
||||
|
||||
|
Reference in New Issue
Block a user