diff --git a/bugzoo/__init__.py b/bugzoo/__init__.py index 00bdd4c0f..779ad2be6 100644 --- a/bugzoo/__init__.py +++ b/bugzoo/__init__.py @@ -4,3 +4,6 @@ from bugzoo.core import Language, Patch from bugzoo.core.container import Container from bugzoo.core.bug import Bug + +import logging +logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/bugzoo/client/__init__.py b/bugzoo/client/__init__.py index 1fdfe3cd5..d8e166238 100644 --- a/bugzoo/client/__init__.py +++ b/bugzoo/client/__init__.py @@ -1,3 +1,5 @@ +import logging + from .api import APIClient from .bug import BugManager from .container import ContainerManager @@ -5,6 +7,8 @@ from .dockerm import DockerManager from ..exceptions import ConnectionFailure +logger = logging.getLogger(__name__) # type: logging.Logger + __all__ = ['Client'] diff --git a/bugzoo/client/api.py b/bugzoo/client/api.py index e8900020d..7b3559e77 100644 --- a/bugzoo/client/api.py +++ b/bugzoo/client/api.py @@ -9,6 +9,10 @@ from ..exceptions import ConnectionFailure, UnexpectedResponse, BugZooException +logger = logging.getLogger(__name__) # type: logging.Logger + +__all__ = ['APIClient'] + class APIClient(object): def __init__(self, @@ -32,11 +36,12 @@ def __init__(self, """ assert timeout_connection > 0 - logging.basicConfig(level=logging.DEBUG) - self.__logger = logging.getLogger('api') self.__base_url = base_url # attempt to establish a connection + logging.info("Attempting to establish connection to %s within %d seconds", # noqa: pycodestyle + base_url, + timeout_connection) url = self._url("status") time_started = timer() connected = False @@ -44,6 +49,8 @@ def __init__(self, time_running = timer() - time_started time_left = timeout_connection - time_running if time_left <= 0.0: + logger.error("Failed to establish connection to server: %s", + base_url) raise ConnectionFailure try: r = requests.get(url, timeout=time_left) @@ -51,8 +58,11 @@ def __init__(self, except requests.exceptions.ConnectionError: time.sleep(1.0) except requests.exceptions.Timeout: + logger.error("Failed to establish connection to server: %s", + base_url) raise ConnectionFailure time.sleep(0.05) + logging.info("Established connection to server: %s", base_url) def _url(self, path: str) -> str: """ @@ -84,7 +94,7 @@ def get(self, json: Optional[Any] = None ) -> requests.Response: url = self._url(path) - self.__logger.info('GET: %s', url) + logger.info('GET: %s', url) return requests.get(url, json=json) def post(self, @@ -93,12 +103,12 @@ def post(self, json: Optional[Any] = None ) -> requests.Response: url = self._url(path) - self.__logger.info('POST: %s', url) + logger.info('POST: %s', url) return requests.post(url, json=json) def put(self, path: str, **kwargs) -> requests.Response: url = self._url(path) - self.__logger.info('PUT: %s', url) + logger.info('PUT: %s', url) return requests.put(url, **kwargs) def patch(self, @@ -107,7 +117,7 @@ def patch(self, **kwargs ) -> requests.Response: url = self._url(path) - self.__logger.info('PATCH: %s', url) + logger.info('PATCH: %s', url) return requests.patch(url, data=data, **kwargs) def delete(self, @@ -116,5 +126,5 @@ def delete(self, json: Optional[Any] = None ) -> requests.Response: url = self._url(path) - self.__logger.info('DELETE: %s', url) + logger.info('DELETE: %s', url) return requests.delete(url, json=json) diff --git a/bugzoo/client/bug.py b/bugzoo/client/bug.py index 0ad95969f..3ecf5319f 100644 --- a/bugzoo/client/bug.py +++ b/bugzoo/client/bug.py @@ -5,24 +5,26 @@ from ..core.bug import Bug from ..core.coverage import TestSuiteCoverage +logger = logging.getLogger(__name__) # type: logging.Logger + +__all__ = ['BugManager'] + class BugManager(object): def __init__(self, api: APIClient) -> None: - logging.basicConfig(level=logging.DEBUG) - self.__logger = logging.getLogger('bug') self.__api = api def __getitem__(self, name: str) -> Bug: - self.__logger.info("Fetching information for bug: %s", name) + logger.info("Fetching information for bug: %s", name) r = self.__api.get('bugs/{}'.format(name)) if r.status_code == 200: return Bug.from_dict(r.json()) if r.status_code == 404: - self.__logger.info("Bug not found: %s", name) + logger.info("Bug not found: %s", name) raise KeyError("no bug found with given name: {}".format(name)) - self.__logger.info("Unexpected API response when retrieving bug: %s", name) + logger.info("Unexpected API response when retrieving bug: %s", name) self.__api.handle_erroneous_response(r) def __iter__(self) -> Iterator[str]: diff --git a/bugzoo/client/container.py b/bugzoo/client/container.py index 684fe44ba..9fad7f539 100644 --- a/bugzoo/client/container.py +++ b/bugzoo/client/container.py @@ -10,11 +10,13 @@ from ..testing import TestCase, TestOutcome from ..cmd import ExecResponse +logger = logging.getLogger(__name__) # type: logging.Logger + +__all__ = ['ContainerManager'] + class ContainerManager(object): def __init__(self, api: APIClient) -> None: - logging.basicConfig(level=logging.DEBUG) - self.__logger = logging.getLogger('container') self.__api = api def __getitem__(self, uid: str) -> Container: @@ -93,12 +95,12 @@ def __iter__(self) -> Iterator[str]: self.__api.handle_erroneous_response(r) def provision(self, bug: Bug) -> Container: - self.__logger.info("provisioning container for bug: %s", bug.name) + logger.info("provisioning container for bug: %s", bug.name) r = self.__api.post('bugs/{}/provision'.format(bug.name)) if r.status_code == 200: container = Container.from_dict(r.json()) - self.__logger.info("provisioned container (id: %s) for bug: %s", + logger.info("provisioned container (id: %s) for bug: %s", container.uid, bug.name) return container diff --git a/bugzoo/client/dockerm.py b/bugzoo/client/dockerm.py index 103c0e6ee..7f2e0d188 100644 --- a/bugzoo/client/dockerm.py +++ b/bugzoo/client/dockerm.py @@ -1,5 +1,9 @@ +import logging + from .api import APIClient +logger = logging.getLogger(__name__) # type: logging.Logger + __all__ = ['DockerManager'] @@ -17,7 +21,14 @@ def delete_image(self, name: str) -> None: Parameters: name: the name of the Docker image. """ + logger.info("Deleting Docker image: %s", name) path = "/docker/images/{}".format(name) response = self.__api.delete(path) if response.status_code != 204: - self.__api.handle_erroneous_response(response) + try: + self.__api.handle_erroneous_response(response) + except Exception: + logger.exception("Failed to delete Docker image: %s", name) + raise + else: + logger.info("Deleted Docker image: %s", name) diff --git a/bugzoo/client/file.py b/bugzoo/client/file.py index bde89ea56..292c07e94 100644 --- a/bugzoo/client/file.py +++ b/bugzoo/client/file.py @@ -6,14 +6,16 @@ from .bug import BugManager from ..core.container import Container +logger = logging.getLogger(__name__) # type: logging.Logger + +__all__ = ['FileManager'] + class FileManager(object): def __init__(self, api: APIClient, mgr_bug: BugManager ) -> None: - logging.basicConfig(level=logging.DEBUG) - self.__logger = logging.getLogger('files') self.__api = api self.__mgr_bug = mgr_bug diff --git a/bugzoo/cmd.py b/bugzoo/cmd.py index 6d0449a21..65d48a2d1 100644 --- a/bugzoo/cmd.py +++ b/bugzoo/cmd.py @@ -1,6 +1,10 @@ -import docker +from typing import Optional import sys +import docker + +__all__ = ['PendingExecResponse', 'ExecResponse'] + class PendingExecResponse(object): def __init__(self, exec_response, output) -> None: @@ -21,7 +25,7 @@ def output(self): return self.__output @property - def exit_code(self) -> int: + def exit_code(self) -> Optional[int]: if self.running: return None client = docker.from_env() diff --git a/bugzoo/compiler/__init__.py b/bugzoo/compiler/__init__.py index c9af59b5d..28401e0a5 100644 --- a/bugzoo/compiler/__init__.py +++ b/bugzoo/compiler/__init__.py @@ -1,5 +1,6 @@ from typing import Optional -from bugzoo.cmd import ExecResponse + +from ..cmd import ExecResponse class CompilationOutcome(object): @@ -140,7 +141,7 @@ def __init__(self, self.__time_limit = time_limit @property - def context(self) -> str: + def context(self) -> Optional[str]: """ The directory from which the compilation command is called. """ @@ -191,7 +192,10 @@ def compile(self, # type: ignore """ See `Compiler.compile` """ - return self.__compile(manager_container, container, self.__command, verbose) + return self.__compile(manager_container, + container, + self.__command, + verbose) def compile_with_coverage_instrumentation(self, # type: ignore manager_container, @@ -243,7 +247,7 @@ def __init__(self, time_limit=time_limit) @property - def workspace(self) -> str: + def workspace(self) -> Optional[str]: return self.context def to_dict(self): diff --git a/bugzoo/core/build.py b/bugzoo/core/build.py index 37d4ae328..839fa053b 100644 --- a/bugzoo/core/build.py +++ b/bugzoo/core/build.py @@ -44,7 +44,7 @@ def source(self) -> Optional[str]: return self.__source @property - def depends_on(self) -> str: + def depends_on(self) -> Optional[str]: """ The name of the Docker image that the construction of the image associated with these build instructions depends on. If no such diff --git a/bugzoo/manager.py b/bugzoo/manager.py index 3d7b5639f..775470e12 100644 --- a/bugzoo/manager.py +++ b/bugzoo/manager.py @@ -1,6 +1,7 @@ from typing import Iterator import os import logging +import logging.handlers import time from docker import DockerClient @@ -14,6 +15,10 @@ from .mgr.coverage import CoverageManager from .mgr.file import FileManager +logger = logging.getLogger(__name__) + +__all__ = ['BugZoo'] + class BugZoo(object): """ @@ -31,7 +36,7 @@ def __init__(self, path=None) -> None: """ # TODO support windows if path is None: - default_path = os.path.join(os.environ.get('HOME'), '.bugzoo') + default_path = os.path.join(os.environ['HOME'], '.bugzoo') path = os.environ.get('BUGZOO_PATH', default_path) self.__path = path path_logs = os.path.join(path, 'logs') @@ -46,15 +51,14 @@ def __init__(self, path=None) -> None: # enable logging # TODO allow users to control output and verbosity + # TODO move to server? timestamp = time.strftime('%Y%m%d-%H%M%S', time.gmtime()) log_fn = 'logs/{}.bugzoo.log'.format(timestamp) log_fn = os.path.join(self.__path, log_fn) - logging.basicConfig(filename=log_fn, - filemode='w', - level=logging.INFO) - self.__logger = logging.getLogger('bugzoo') - self.__logger.setLevel(logging.DEBUG) - self.__logger.info('Logging to: %s', log_fn) + log_fn_handler = logging.handlers.WatchedFileHandler(log_fn) + + bz_log = logging.getLogger('bugzoo') # type: logging.Logger + bz_log.addHandler(log_fn_handler) self.__docker = docker_client_from_env(timeout=120) self.__mgr_build = BuildManager(self.__docker) @@ -72,13 +76,6 @@ def docker(self) -> DockerClient: """ return self.__docker - @property - def logger(self) -> logging.Logger: - """ - The root logging mechanism for BugZoo. - """ - return self.__logger - @property def path(self) -> str: """ diff --git a/bugzoo/mgr/container.py b/bugzoo/mgr/container.py index ed89d4543..6c097d0d0 100644 --- a/bugzoo/mgr/container.py +++ b/bugzoo/mgr/container.py @@ -9,6 +9,7 @@ import os import uuid import copy +import logging import docker @@ -21,15 +22,17 @@ from ..testing import TestCase, TestOutcome from ..cmd import ExecResponse, PendingExecResponse +logger = logging.getLogger(__name__) + +__all__ = ['ContainerManager'] + class ContainerManager(object): - def __init__(self, installation: 'BugZoo'): + def __init__(self, installation: 'BugZoo') -> None: self.__installation = installation - self.__logger = installation.logger.getChild('container') - self.__client_docker = installation.docker - self.__api_docker = self.__client_docker.api + self.__client_docker = installation.docker # type: docker.Client + self.__api_docker = self.__client_docker.api # type: docker.APIClient self.__containers = {} - self.__logger = installation.logger.getChild('container') self.__dockerc = {} self.__env_files = {} self.__dockerc_tools = {} @@ -59,7 +62,7 @@ def __delitem__(self, uid: str) -> None: Raises: KeyError: if no container was found with the given UID. """ - self.__logger.info("deleting container: %s", uid) + logger.info("deleting container: %s", uid) try: container = self.__containers[uid] self.__dockerc[uid].remove(force=True) @@ -76,8 +79,8 @@ def __delitem__(self, uid: str) -> None: del self.__containers[uid] except KeyError: - self.__logger.error("failed to delete container: %s [not found]", uid) - self.__logger.info("deleted container: %s", uid) + logger.error("failed to delete container: %s [not found]", uid) + logger.info("deleted container: %s", uid) delete = __delitem__ @@ -242,7 +245,7 @@ def patch(self, container: Container, p: Patch) -> bool: file_container = None dockerc = self.__dockerc[container.uid] bug = self.__installation.bugs[container.bug] - self.__logger.debug("Applying patch to container [%s]:\n%s", + logger.debug("Applying patch to container [%s]:\n%s", container.uid, str(p)) @@ -263,7 +266,7 @@ def patch(self, container: Container, p: Patch) -> bool: cmd = 'sudo chown $(whoami) {} && git apply -p0 "{}"' cmd = cmd.format(file_container, file_container) outcome = self.command(container, cmd, context=bug.source_dir) - self.__logger.debug("Patch application outcome [%s]: (retcode=%d)\n%s", + logger.debug("Patch application outcome [%s]: (retcode=%d)\n%s", container.uid, outcome.code, outcome.output) @@ -395,7 +398,7 @@ def command(self, TimeoutError: if a time limit is given and the command fails to complete within that time. Only supported by blocking calls. """ - logger = self.__logger.getChild(container.uid) + logger = logger.getChild(container.uid) bug = self.__installation.bugs[container.bug] # TODO: we need a better long-term alternative @@ -452,6 +455,18 @@ def persist(self, container: Container, image: str) -> None: ImageAlreadyExists: if the image name is already in use by another Docker image on this server. """ - docker_container = self.__dockerc[container.uid] - # TODO check if image already exists - docker_container.commit(repository=image) + logger = logger.getChild(container.uid) + logger.info("Persisting container as a Docker image: %s", image) + try: + docker_container = self.__dockerc[container.uid] + except KeyError: + logger.exception("Failed to persist container: container no longer exists.") # noqa: pycodestyle + raise + try: + _ = self.__client_docker.images.get(image) + logger.error("Failed to persist container: image, '%s', already exists.", # noqa: pycodestyle + image) + raise ImageAlreadyExists(image) + except docker.errors.ImageNotFound: + docker_container.commit(repository=image) + logger.info("Persisted container as a Docker image: %s", image) diff --git a/bugzoo/mgr/coverage.py b/bugzoo/mgr/coverage.py index 13e9ac61a..2c94e232f 100644 --- a/bugzoo/mgr/coverage.py +++ b/bugzoo/mgr/coverage.py @@ -3,6 +3,7 @@ import tempfile import os import warnings +import logging import xml.etree.ElementTree as ET from ..core.fileline import FileLineSet @@ -11,6 +12,10 @@ TestCoverage from ..testing.base import TestCase +logger = logging.getLogger(__name__) + +__all__ = ['CoverageManager'] + class CoverageManager(object): INSTRUMENTATION = ( @@ -56,7 +61,7 @@ def _from_gcovr_xml_string(self, the set of file-lines that are stated as covered by the given report. """ - logger = self.__logger.getChild(container.id) + logger = logger.getChild(container.id) mgr_ctr = self.__installation.containers mgr_bug = self.__installation.bugs bug = mgr_bug[container.bug] @@ -120,9 +125,8 @@ def resolve_path(fn_rel: str) -> str: return FileLineSet(files_to_lines) - def __init__(self, installation: 'BugZoo'): + def __init__(self, installation: 'BugZoo') -> None: self.__installation = installation # type: BugZoo - self.__logger = installation.logger.getChild('coverage') def coverage(self, container: Container, @@ -169,7 +173,7 @@ def instrument(self, Raises: Exception: if an absolute file path is provided. """ - self.__logger.info("instrumenting container: %s", container.uid) + logger.info("instrumenting container: %s", container.uid) mgr_ctr = self.__installation.containers mgr_bug = self.__installation.bugs bug = mgr_bug[container.bug] @@ -189,7 +193,7 @@ def instrument(self, dir_source = bug.source_dir for fn_src in files_to_instrument: fn_src = os.path.join(dir_source, fn_src) - self.__logger.debug("instrumenting file [%s] in container [%s]", + logger.debug("instrumenting file [%s] in container [%s]", fn_src, container.uid) (_, fn_temp) = tempfile.mkstemp(suffix='.bugzoo') try: @@ -211,7 +215,7 @@ def instrument(self, print(outcome.response.output) raise Exception(msg) - self.__logger.info("instrumented container: %s", container.uid) + logger.info("instrumented container: %s", container.uid) def deinstrument(self, container: Container, @@ -249,7 +253,7 @@ def extract(self, code files within the project. Destroys '.gcda' files upon computing coverage. """ - logger = self.__logger.getChild(container.id) + logger = logger.getChild(container.id) mgr_ctr = self.__installation.containers mgr_bug = self.__installation.bugs logger.debug("Extracting coverage information") diff --git a/bugzoo/mgr/source.py b/bugzoo/mgr/source.py index 14f9605a9..84f17bd2f 100644 --- a/bugzoo/mgr/source.py +++ b/bugzoo/mgr/source.py @@ -5,11 +5,11 @@ import urllib.parse import logging import warnings +import shutil +import glob import git import yaml -import shutil -import glob from .build import BuildManager from ..exceptions import NameInUseError, BadManifestFile @@ -21,6 +21,10 @@ from ..core.tool import Tool from ..core.source import Source, SourceContents, RemoteSource, LocalSource +logger = logging.getLogger(__name__) + +__all__ = ['SourceManager'] + class SourceManager(object): """ @@ -29,10 +33,8 @@ class SourceManager(object): seems like overengineering and may create compatibility headaches in the future. """ - def __init__(self, - installation: 'BugZoo'): + def __init__(self, installation: 'BugZoo') -> None: self.__installation = installation - self.__logger = installation.logger.getChild('source') self.__path = os.path.join(installation.path, 'sources') # TODO self.__registry_fn = os.path.join(self.__path, 'registry.yml') @@ -65,7 +67,7 @@ def refresh(self) -> None: """ Reloads all sources that are registered with this server. """ - self.__logger.info('refreshing sources') + logger.info('refreshing sources') for source in list(self): self.unload(source) @@ -81,7 +83,7 @@ def refresh(self) -> None: source = Source.from_dict(source_description) self.load(source) - self.__logger.info('refreshed sources') + logger.info('refreshed sources') def update(self) -> None: """ @@ -101,13 +103,13 @@ def update(self) -> None: source_old.location, source_old.url, version) - self.__logger.info("updated source: %s [%s -> %s]", source_old.name, - source_old.version, - source_new.version) + logger.info("updated source: %s [%s -> %s]", source_old.name, + source_old.version, + source_new.version) self.load(source_new) else: - self.__logger.debug("no updates for source: %s", source_old.name) + logger.debug("no updates for source: %s", source_old.name) # write to disk # TODO local directory may be corrupted if program terminates between @@ -119,13 +121,13 @@ def save(self) -> None: """ Saves the contents of the source manager to disk. """ - self.__logger.info('saving registry to: %s', self.__registry_fn) + logger.info('saving registry to: %s', self.__registry_fn) d = [s.to_dict() for s in self] os.makedirs(self.__path, exist_ok=True) with open(self.__registry_fn, 'w') as f: yaml.dump(d, f, indent=2, default_flow_style=False) - self.__logger.info('saved registry to: %s', self.__registry_fn) + logger.info('saved registry to: %s', self.__registry_fn) def unload(self, source: Source) -> None: """ @@ -133,7 +135,7 @@ def unload(self, source: Source) -> None: and blueprints to also be unloaded. If the given source is not loaded, this function will do nothing. """ - self.__logger.info('unloading source: %s', source.name) + logger.info('unloading source: %s', source.name) try: contents = self.contents(source) del self.__contents[source.name] @@ -150,7 +152,7 @@ def unload(self, source: Source) -> None: self.__installation.tools.remove(tool) except KeyError: pass - self.__logger.info('unloaded source: %s', source.name) + logger.info('unloaded source: %s', source.name) def __parse_blueprint(self, source: Source, fn: str, d: dict) -> BuildInstructions: return BuildInstructions(os.path.dirname(fn), @@ -168,7 +170,7 @@ def __parse_bug(self, source: Source, fn: str, d: dict) -> Bug: files_to_instrument = d['coverage']['files-to-instrument'] else: files_to_instrument = [] - self.__logger.info('"coverage.files-to-instrument" for %s: %s', + logger.info('"coverage.files-to-instrument" for %s: %s', name, files_to_instrument) @@ -201,35 +203,35 @@ def __parse_file(self, # TODO check version if 'version' not in yml: - self.__logger.warning("no version specified in manifest file: %s", fn) + logger.warning("no version specified in manifest file: %s", fn) for description in yml.get('bugs', []): - self.__logger.debug("parsing bug: %s", json.dumps(description)) + logger.debug("parsing bug: %s", json.dumps(description)) try: bug = self.__parse_bug(source, fn, description) - self.__logger.debug("parsed bug: %s", bug.name) + logger.debug("parsed bug: %s", bug.name) bugs.append(bug) except KeyError as e: - self.__logger.error("missing property in bug description: %s", str(e)) + logger.error("missing property in bug description: %s", str(e)) for description in yml.get('blueprints', []): - self.__logger.debug("parsing blueprint: %s", json.dumps(description)) + logger.debug("parsing blueprint: %s", json.dumps(description)) try: blueprint = self.__parse_blueprint(source, fn, description) - self.__logger.debug("parsed blueprint for image: %s", + logger.debug("parsed blueprint for image: %s", blueprint.name) blueprints.append(blueprint) except KeyError as e: - self.__logger.error("missing property in blueprint description: %s", str(e)) + logger.error("missing property in blueprint description: %s", str(e)) for description in yml.get('tools', []): - self.__logger.debug("parsing tool: %s", json.dumps(description)) + logger.debug("parsing tool: %s", json.dumps(description)) try: tool = self.__parse_tool(source, fn, description) - self.__logger.debug("parsed tool: %s", tool.name) + logger.debug("parsed tool: %s", tool.name) tools.append(tool) except KeyError as e: - self.__logger.error("missing property in tool description: %s", str(e)) + logger.error("missing property in tool description: %s", str(e)) def load(self, source: Source) -> None: """ @@ -238,7 +240,7 @@ def load(self, source: Source) -> None: loaded, then that resources for that source are unloaded and reloaded. """ - self.__logger.info('loading source %s at %s', source.name, source.location) + logger.info('loading source %s at %s', source.name, source.location) if source.name in self.__sources: self.unload(source) @@ -250,9 +252,9 @@ def load(self, source: Source) -> None: glob_pattern = '{}/**/*.bugzoo.y*ml'.format(source.location) for fn in glob.iglob(glob_pattern, recursive=True): if fn.endswith('.yml') or fn.endswith('.yaml'): - self.__logger.debug('found manifest file: %s', fn) + logger.debug('found manifest file: %s', fn) self.__parse_file(source, fn, bugs, blueprints, tools) - self.__logger.debug('parsed manifest file: %s', fn) + logger.debug('parsed manifest file: %s', fn) # register contents for bug in bugs: @@ -268,7 +270,7 @@ def load(self, source: Source) -> None: [t.name for t in tools]) self.__sources[source.name] = source self.__contents[source.name] = contents - self.__logger.info("loaded source: %s", source.name) + logger.info("loaded source: %s", source.name) def contents(self, source: Source) -> SourceContents: """ @@ -291,18 +293,18 @@ def add(self, name: str, path_or_url: str) -> Source: IOError: if no directory exists at the given path. IOError: if downloading the remote source failed. (FIXME) """ - self.__logger.info("adding source: %s -> %s", name, path_or_url) + logger.info("adding source: %s -> %s", name, path_or_url) if name in self.__sources: - self.__logger.info("name already used by existing source: %s", name) + logger.info("name already used by existing source: %s", name) raise NameInUseError(name) is_url = False try: scheme = urllib.parse.urlparse(path_or_url).scheme is_url = scheme in ['http', 'https'] - self.__logger.debug("source determined to be remote: %s", path_or_url) + logger.debug("source determined to be remote: %s", path_or_url) except ValueError: - self.__logger.debug("source determined to be local: %s", path_or_url) + logger.debug("source determined to be local: %s", path_or_url) if is_url: url = path_or_url @@ -317,15 +319,15 @@ def add(self, name: str, path_or_url: str) -> Source: shutil.rmtree(path, ignore_errors=True) try: # TODO shallow clone - self.__logger.debug("cloning repository %s to %s", url, path) + logger.debug("cloning repository %s to %s", url, path) repo = git.Repo.clone_from(url, path) - self.__logger.debug("cloned repository %s to %s", url, path) + logger.debug("cloned repository %s to %s", url, path) sha = repo.head.object.hexsha version = repo.git.rev_parse(sha, short=8) except: # TODO determine error type shutil.rmtree(path, ignore_errors=True) - self.__logger.error("failed to download remote source to local: %s -> %s", url, path) + logger.error("failed to download remote source to local: %s -> %s", url, path) raise IOError("failed to download remote source to local installation: '{}' -> '{}'".format(url, path)) source = RemoteSource(name, path, url, version) @@ -337,7 +339,7 @@ def add(self, name: str, path_or_url: str) -> Source: self.load(source) self.save() - self.__logger.info('added source: %s', name) + logger.info('added source: %s', name) def remove(self, source: Source) -> None: """ diff --git a/bugzoo/server/__init__.py b/bugzoo/server/__init__.py index 70c484f7b..9d56bf8ce 100644 --- a/bugzoo/server/__init__.py +++ b/bugzoo/server/__init__.py @@ -14,7 +14,8 @@ from ..exceptions import * from ..client import Client -daemon = None # type: BugZoo +# FIXME let's avoid storing the actual server in a global var +daemon = None # type: Any app = flask.Flask(__name__)