Cleanup: Changes suggested by @sybren

This commit is contained in:
gandalf3
2017-07-05 18:40:32 -07:00
parent 1cefb4eab6
commit f20264c963
2 changed files with 78 additions and 73 deletions

View File

@@ -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)

View File

@@ -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)