<commit_before>from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from __future__ import print_function

import appdirs
from argh import ArghParser
from argh.decorators import arg, named

import os
import sys

from .. import tech

from ..pkg.workspace import Workspace, CurrentDirWorkspace
from ..pkg.archive import Archive
from ..pkg import metakey
from .. import db

from .. import PACKAGE, VERSION
from ..translations import Peer, add_translation
from .. import repos
from .. import channels


Path = tech.fs.Path
timestamp = tech.timestamp.timestamp

ERROR_EXIT = 1

WORKSPACE_HELP = 'workspace directory'
WORKSPACE_METAVAR = 'DIRECTORY'


def opt_workspace(func):
    '''
    Define `workspace` as option, defaulting to current directory
    '''
    decorate = arg(
        '--workspace', metavar=WORKSPACE_METAVAR,
        type=Workspace, default=CurrentDirWorkspace(),
        help=WORKSPACE_HELP)
    return decorate(func)


def arg_workspace(func):
    '''
    Define `workspace` argument, defaulting to current directory
    '''
    decorate = arg(
        'workspace', nargs='?', metavar=WORKSPACE_METAVAR,
        type=Workspace, default=CurrentDirWorkspace(),
        help=WORKSPACE_HELP)
    return decorate(func)


def arg_new_workspace(func):
    '''
    Define mandatory `workspace` argument (without default)
    '''
    decorate = arg(
        'workspace', type=Workspace, metavar=WORKSPACE_METAVAR,
        help=WORKSPACE_HELP)
    return decorate(func)


def die(msg):
    sys.stderr.write('ERROR: ')
    sys.stderr.write(msg)
    sys.stderr.write('\n')
    sys.exit(ERROR_EXIT)


def assert_valid_workspace(workspace):
    if not workspace.is_valid:
        die('{} is not a valid workspace'.format(workspace.directory))


def assert_may_be_valid_name(name):
    valid_syntax = (
        name
        and os.path.sep not in name
        and '/' not in name
        and '\\' not in name
        and ':' not in name
    )
    if not valid_syntax:
        die('Invalid name "{}"'.format(name))

    if Peer.self().knows_about(name):
        die('"{}" is already used, rename it if you insist'.format(name))


# @command
@arg_new_workspace
def new(workspace):
    '''
    Create new package directory layout.
    '''
    uuid = tech.identifier.uuid()

    assert_may_be_valid_name(workspace.package_name)
    add_translation(workspace.package_name, uuid)

    workspace.create(uuid)
    print('Created {}'.format(workspace.package_name))


def get_channel():
    return channels.AllAvailable(repos.get_all())


# @command
@arg(
    'workspace', nargs='?', type=Workspace, default=None,
    metavar=WORKSPACE_METAVAR, help='workspace directory')
@arg(
    'package_ref',
    metavar='PACKAGE', help='package name or file name')
def develop(package_ref, workspace, mount=False):
    '''
    Unpack a package as a source tree.

    Package directory layout is created, but only the source files are
    extracted.
    '''
    # TODO: #10 names for packages
    if os.path.isfile(package_ref):
        package = Archive(package_ref)
        if workspace is None:
            workspace = Workspace(
                os.path.basename(os.path.splitext(package_ref)[0]))
    else:
        from ..pkg.spec import parse
        package_spec = parse(package_ref)
        peer = Peer.by_name(package_spec.peer)
        package_translation = peer.get_translation(package_spec.name)
        uuid = package_translation.package_uuid
        try:
            package = (
                get_channel()
                .get_package(uuid, package_spec.version, package_spec.offset))
        except LookupError:
            die('Package not found!')

        if workspace is None:
            workspace = Workspace(package_spec.name)
    dir = workspace.directory

    package.unpack_to(workspace)

    assert workspace.is_valid

    if mount:
        load_inputs(workspace)

    print('Extracted source into {}'.format(dir))
    print_mounts(directory=dir)


# @command
@opt_workspace
def pack(workspace=CurrentDirWorkspace()):
    '''
    Create a new archive from the workspace.
    '''<commit_msg>develop: extracted argument type PackageReference<commit_after>from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from __future__ import print_function

import appdirs
from argh import ArghParser
from argh.decorators import arg, named

import os
import sys

from .. import tech

from ..pkg.workspace import Workspace, CurrentDirWorkspace
from ..pkg.archive import Archive
from ..pkg.spec import parse as parse_package_spec
from ..pkg import metakey
from .. import db

from .. import PACKAGE, VERSION
from ..translations import Peer, add_translation
from .. import repos
from .. import channels


Path = tech.fs.Path
timestamp = tech.timestamp.timestamp

ERROR_EXIT = 1

WORKSPACE_HELP = 'workspace directory'
WORKSPACE_METAVAR = 'DIRECTORY'


def opt_workspace(func):
    '''
    Define `workspace` as option, defaulting to current directory
    '''
    decorate = arg(
        '--workspace', metavar=WORKSPACE_METAVAR,
        type=Workspace, default=CurrentDirWorkspace(),
        help=WORKSPACE_HELP)
    return decorate(func)


def arg_workspace(func):
    '''
    Define `workspace` argument, defaulting to current directory
    '''
    decorate = arg(
        'workspace', nargs='?', metavar=WORKSPACE_METAVAR,
        type=Workspace, default=CurrentDirWorkspace(),
        help=WORKSPACE_HELP)
    return decorate(func)


def arg_new_workspace(func):
    '''
    Define mandatory `workspace` argument (without default)
    '''
    decorate = arg(
        'workspace', type=Workspace, metavar=WORKSPACE_METAVAR,
        help=WORKSPACE_HELP)
    return decorate(func)


def die(msg):
    sys.stderr.write('ERROR: ')
    sys.stderr.write(msg)
    sys.stderr.write('\n')
    sys.exit(ERROR_EXIT)


def assert_valid_workspace(workspace):
    if not workspace.is_valid:
        die('{} is not a valid workspace'.format(workspace.directory))


def assert_may_be_valid_name(name):
    valid_syntax = (
        name
        and os.path.sep not in name
        and '/' not in name
        and '\\' not in name
        and ':' not in name
    )
    if not valid_syntax:
        die('Invalid name "{}"'.format(name))

    if Peer.self().knows_about(name):
        die('"{}" is already used, rename it if you insist'.format(name))


# @command
@arg_new_workspace
def new(workspace):
    '''
    Create new package directory layout.
    '''
    uuid = tech.identifier.uuid()

    assert_may_be_valid_name(workspace.package_name)
    add_translation(workspace.package_name, uuid)

    workspace.create(uuid)
    print('Created {}'.format(workspace.package_name))


def get_channel():
    return channels.AllAvailable(repos.get_all())


class PackageReference(object):
    def __init__(self, package_reference):
        self.package_reference = package_reference

    @property
    def package(self):
        if os.path.isfile(self.package_reference):
            return Archive(self.package_reference)

        package_spec = parse_package_spec(self.package_reference)
        peer = Peer.by_name(package_spec.peer)
        package_translation = peer.get_translation(package_spec.name)
        uuid = package_translation.package_uuid
        package = (
            get_channel()
            .get_package(uuid, package_spec.version, package_spec.offset))
        return package

    @property
    def default_workspace(self):
        if os.path.isfile(self.package_reference):
            archive_filename = os.path.basename(self.package_reference)[0]
            workspace_dir = os.path.splitext(archive_filename)
        else:
            package_spec = parse_package_spec(self.package_reference)
            workspace_dir = package_spec.name
        return Workspace(workspace_dir)


class DefaultArgSentinel(object):
    '''
    I am a sentinel for @argh.arg default values.

    I.e. I can tell you, that you got the default value.

    I also provide sensible description for the default value.
    '''

    def __init__(self, description):
        self.description = description

    def __repr__(self):
        return self.description

DERIVE_FROM_PACKAGE_NAME = DefaultArgSentinel('derive from package name')


# @command
@arg(
    'workspace', nargs='?', type=Workspace, default=DERIVE_FROM_PACKAGE_NAME,
    metavar=WORKSPACE_METAVAR, help='workspace directory')
@arg(
    'package_ref', type=PackageReference,
    metavar='PACKAGE', help='package name or file name')
def develop(package_ref, workspace, mount=False):
    '''
    Unpack a package as a source tree.

    Package directory layout is created, but only the source files are
    extracted.
    '''
    # TODO: #10 names for packages
    try:
        package = package_ref.package
    except LookupError:
        die('Package not found!')
    if workspace is DERIVE_FROM_PACKAGE_NAME:
        workspace = package_ref.default_workspace

    package.unpack_to(workspace)
    assert workspace.is_valid

    if mount:
        load_inputs(workspace)

    dir = workspace.directory
    print('Extracted source into {}'.format(dir))
    print_mounts(directory=dir)


# @command
@opt_workspace
def pack(workspace=CurrentDirWorkspace()):
    '''
    Create a new archive from the workspace.
    '''
{'fraction_non_alphanumeric': 0.09722518676627534, 'fraction_numerical': 0.0008537886872998933, 'mean_word_length': 3.2230734565119423, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '862749', 'n_tokens_mistral': 2812, 'n_tokens_neox': 2784, 'n_words': 705}