#!/usr/bin/env python3 # HACK: seems 'requests' module bundled with blender isn't bundled with 'idna' module. So force system python for now import sys sys.path.insert(0, '/usr/lib/python3.6/site-packages') from pathlib import Path import requests import json import logging log = logging.getLogger(__name__) # log.level = logging.DEBUG class RepositoryError(Exception): """ Superclass for repository-related exceptions """ class MissingURLError(RepositoryError): """ Thrown when the repository needs a URL but doesn't have one """ # class RepositoryNotFound( class Package: """ Stores package methods and metadata """ def __init__(self, package_dict:dict = None): self.from_dict(package_dict) def to_dict(self) -> dict: """ Return a dict representation of the package """ return { 'bl_info': self.bl_info, 'url': self.url, } def from_dict(self, package_dict: dict): """ Get attributes from a dict such as produced by `to_dict` """ if package_dict is None: package_dict = {} for attr in ('name', 'url', 'bl_info'): setattr(self, attr, package_dict.get(attr)) class Repository: """ Stores repository metadata (including packages) """ def __init__(self, repo_dict:dict = None): self.from_dict(repo_dict) def refresh(self): """ Requests repo.json from URL and embeds etag/last-modification headers """ log.debug(self.url) if self.url is None: raise MissingURLError("Cannot refresh repository without a URL") log.debug("Refreshing repository from %s", self.url) req_headers = {} # Do things this way to avoid adding empty objects/None to the req_headers dict if self._headers: try: req_headers['If-None-Match'] = self._headers['etag'] except KeyError: pass try: req_headers['If-Modified-Since'] = self._headers['last-modified'] except KeyError: pass #try resp = requests.get(self.url, headers=req_headers) repodict = json.loads(resp.json()) repodict['etag'] = resp.headers.get('etag') repodict['last-modified'] = resp.headers.get('last-modified') self.from_dict(repodict) def to_dict(self) -> dict: """ Return a dict representation of the repository """ return { 'name': self.name, 'packages': [p.to_dict() for p in self.packages], 'url': self.url, '_headers': self._headers, } def from_dict(self, repodict: dict): """ Get attributes from a dict such as produced by `to_dict` """ if repodict is None: repodict = {} for attr in ('name', 'url', 'packages', '_headers'): if attr == 'package': value = set(Package(pkg) for pkg in repodict.get('packages', [])) else: value = repodict.get(attr) setattr(self, attr, value) 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.to_dict(), repo_file, indent=4, sort_keys=True) log.info("repo.json written to %s" % path)