Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ jobs:
- checkout

- run:
name: Lint code (non-fatal)
name: Lint code
command: |
set +e
tox -e flake8,pylint
exit 0

- run:
name: Run tox
Expand Down
6 changes: 2 additions & 4 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
[MASTER]

# Temporary disable list
disable=invalid-name,unused-wildcard-import,wildcard-import,unused-import,wrong-import-order,missing-docstring,no-self-use,unused-argument,unidiomatic-typecheck,too-many-branches,too-many-arguments,too-many-locals,len-as-condition,line-too-long,too-many-instance-attributes,too-few-public-methods

# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
Expand Down Expand Up @@ -395,7 +392,8 @@ good-names=i,
k,
ex,
Run,
_
_,
db

# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
Expand Down
11 changes: 7 additions & 4 deletions packagemega/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
"""Export all PackageMega components."""

from .repo import *
from .base_recipe import *
from .source_file import *
from .constructed_file import *
# flake8: noqa

from .repo import Repo
from .base_recipe import BaseRecipe
from .source_file import SourceFile
from .constructed_file import ConstructedFile
11 changes: 8 additions & 3 deletions packagemega/base_recipe.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
"""Base PackageMega recipe class."""

from .repo import Repo


class BaseRecipe:
class BaseRecipe: # pylint: disable=too-few-public-methods
"""Base PackageMega recipe class."""

def __init__(self):
self.repo = Repo.loadRepo()
"""Initialize recipe."""
self.repo = Repo.load_repo()

def makeRecipe(self):
def make_recipe(self):
"""Create the recipe."""
raise NotImplementedError()
4 changes: 3 additions & 1 deletion packagemega/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
from .cli import *
"""Comand Line Interface for PackageMega modules."""

from .cli import main # noqa: F401
72 changes: 39 additions & 33 deletions packagemega/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,44 @@

import click
from packagemega import Repo
from packagemega.mini_language import processOperand
from packagemega.mini_language import process_operand
from packagemega.custom_errors import UnresolvableOperandError


version = {}
version_path = os.path.join(os.path.dirname(__file__), '../version.py')
version = {} # pylint: disable=invalid-name
version_path = os.path.join(os.path.dirname(__file__), '../version.py') # pylint: disable=invalid-name
with open(version_path) as version_file:
exec(version_file.read(), version)
exec(version_file.read(), version) # pylint: disable=exec-used


@click.group()
@click.version_option(version['__version__'])
def main():
pass
"""Enter PackageMega CLI."""


###############################################################################


def tableStatus(tblName, statusMap):
'''Check if records in a table are valid and print a report to stdout.'''
sys.stdout.write('\n{} {}... '.format(len(statusMap), tblName))
allGood = True
for name, status in statusMap.items():
def table_status(tbl_name, status_map):
"""Check if records in a table are valid and print a report to stdout."""
sys.stdout.write('\n{} {}... '.format(len(status_map), tbl_name))
all_good = True
for name, status in status_map.items():
if not status:
allGood = False
all_good = False
sys.stdout.write('\n - {} failed'.format(name))
if allGood:
if all_good:
sys.stdout.write('all good.')


@main.command()
def status():
repo = Repo.loadRepo()
@main.command(name='status')
def pm_status():
"""Print PackageMega repository status report to stdout."""
repo = Repo.load_repo()
sys.stdout.write('Checking status')
for tblName, statusMap in repo.dbStatus().items():
tableStatus(tblName, statusMap)
for tbl_name, status_map in repo.db_status().items():
table_status(tbl_name, status_map)
sys.stdout.write('\nDone\n')

###############################################################################
Expand All @@ -52,50 +53,55 @@ def status():
help='Install recipe with symlinks')
@click.argument('uri')
def add(dev, uri):
repo = Repo.loadRepo()
repo.addFromLocal(uri, dev=dev)
"""Add URI to PackageMega repository."""
repo = Repo.load_repo()
repo.add_from_local(uri, dev=dev)

###############################################################################


@main.command()
@click.argument('name')
def install(name):
repo = Repo.loadRepo()
repo.makeRecipe(name)
"""Install PackageMega recipe."""
repo = Repo.load_repo()
repo.make_recipe(name)

###############################################################################


@main.group()
def view():
pass
"""Enter group of PackageMega view commands."""


@main.command(name='recipe')
def viewRecipes():
repo = Repo.loadRepo()
for recipe in repo.allRecipes():
def view_recipes():
"""View all recipes present in PackageMega repository."""
repo = Repo.load_repo()
for recipe in repo.all_recipes():
print(recipe)

###############################################################################


def printAllDatabases(repo):
for db in repo.allDatabases():
def print_all_databases(repo):
"""Print the name of all databased present in repository."""
for db in repo.all_databases():
print(db.name)


@main.command(name='database')
@click.argument('operands', nargs=-1)
def viewDatabase(operands):
repo = Repo.loadRepo()
if len(operands) == 0:
printAllDatabases(repo)
def view_database(operands):
"""Print database names filtered by operand arguments."""
repo = Repo.load_repo()
if not operands:
print_all_databases(repo)
for operand in operands:
try:
el = processOperand(repo, operand, stringify=True)
print(el)
element = process_operand(repo, operand, stringify=True)
print(element)
except UnresolvableOperandError:
print('{} could not be resolved.'.format(operand), file=sys.stderr)

Expand Down
42 changes: 15 additions & 27 deletions packagemega/constructed_file.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,20 @@
from gimme_input import UserInput, BoolUserInput
from .custom_errors import UnresolvableFileError
import os.path
"""File subclass sourced from a constructor hook."""

class ConstructedFile:
from .file import PMFile

def __init__(self, repo, filename, *args):
self.filename = filename
self.hook = None
if len(args) > 0:
self.hook = args[0]
self._filepath = None
self.repo = repo

def _askUserForFile(self):
_filepath = None
msg = 'Is {} already on this system?'.format(self.filename)
if self.hook is None or BoolUserInput(msg, False).resolve():
msg = 'Please indicate where {} is stored'.format(self.filename)
_filepath = UserInput(msg).resolve()
return _filepath
class ConstructedFile(PMFile):
"""File subclass sourced from a constructor hook."""

def resolve(self):
actualFile = self._askUserForFile()
if actualFile is None and self.hook is not None:
actualFile = self.hook()
if actualFile is None:
raise UnresolvableFileError()
self._filepath = os.path.abspath(actualFile)
def __init__(self, *args, hook=None, **kwargs):
"""Initialize by storing the constructor hook."""
super().__init__(*args, **kwargs)
self.hook = hook

def filepath(self):
return self._filepath
def _resolver(self):
"""Return constructor hook."""
return self.hook

def _resolve_actual_file(self):
"""Resolve file by calling hook."""
return self.hook()
18 changes: 16 additions & 2 deletions packagemega/custom_errors.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
"""Custom PackageMega errors and exceptions."""


class UnresolvableOperandError(Exception):
pass
"""Raised when database operand cannot be resolved."""


class UnresolvableOperandLevel(Exception):
"""Raised when too many operand levels are supplied."""


class UnresolvableFileError(Exception):
pass
"""Raised when a file source cannot be resolved."""


class RecipeNotFoundError(Exception):
"""Raised when a missing recipe is requested."""


class InvalidRecipeURI(Exception):
"""Raised when an invalid recipe URI is supplied."""
47 changes: 47 additions & 0 deletions packagemega/file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Base PackageMega file class."""

import os.path
from gimme_input import UserInput, BoolUserInput

from .custom_errors import UnresolvableFileError


class PMFile:
"""Base PackageMega file class."""

def __init__(self, repo, filename, *args, **kwargs):
"""Initialze PMFile with repo and filename this file is to resolve to."""
super().__init__(*args, **kwargs)
self.repo = repo
self.filename = filename
self._filepath = None

def _ask_user_for_file(self):
"""Prompt user for existing file path."""
_filepath = None
msg = 'Is {} already on this system?'.format(self.filename)
if self._resolver() is None or BoolUserInput(msg, False).resolve():
msg = 'Please indicate where {} is stored'.format(self.filename)
_filepath = UserInput(msg).resolve()
return _filepath

def _resolver(self):
"""Return subclass-specific file resolver."""
raise NotImplementedError()

def _resolve_actual_file(self):
"""Resolve file in subclass-specific way."""
raise NotImplementedError()

def resolve(self):
"""Create file from subclass-specific resolver."""
actual_file = self._ask_user_for_file()
if actual_file is None and self._resolver() is not None:
actual_file = self._resolve_actual_file()
if actual_file is None:
raise UnresolvableFileError()
self._filepath = os.path.abspath(actual_file)

def filepath(self):
"""Expose private _filepath as read-only."""
return self._filepath
Loading