Skip to content

Commit

Permalink
- implements source_url and source_label
Browse files Browse the repository at this point in the history
- fixes script guessing on windows
- adds py-info for reading package metadata
  • Loading branch information
a-tal committed Jul 8, 2015
1 parent 6255411 commit 7c0e177
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 10 deletions.
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Pypackage is a collection of python packaging applications including:

py-build
py-develop
py-info
py-install
py-setup
py-test
Expand Down
25 changes: 25 additions & 0 deletions pypackage/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@


import sys
from functools import wraps

from .constants import VERSION


def flags(*args):
Expand Down Expand Up @@ -46,3 +49,25 @@ def __init__(self):
self.version = flags("-v", "--version")

return Options()


def help_and_version(func):
"""Checks for help and/or version flags.
If the user uses -h/--help it will SystemExit with the function docstring
If the user uses -v/--version it will SystemExit with pypackage's version
"""

@wraps(func)
def _quick_help():
"""Inner wrap function, check for flags or pass on to func."""

if flags("-h", "--help"):
raise SystemExit("\n".join([l.replace(" ", "", 1).rstrip() for
l in func.__doc__.splitlines()]))
elif flags("-v", "--version"):
raise SystemExit(VERSION)
else:
return func()

return _quick_help
47 changes: 39 additions & 8 deletions pypackage/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
Each command's docstring is used as additional help if the -h flag is used.
Setup is a bit of a snowflake in that it doesn't build anything, takes no args.
Info does not take any arguments, only looks up packages by name.
"""


import sys
import pkg_resources

from . import pypackage_setup
from .config import get_config
from .cmdline import flags
from .cmdline import get_options
from .constants import VERSION
from .cmdline import help_and_version


def install():
Expand Down Expand Up @@ -64,6 +66,7 @@ def build():
pypackage_setup(build_commands, options=options, additional=build.__doc__)


@help_and_version
def setup():
"""py-setup will create a setup.py from metadata files and/or inspection.
Expand All @@ -80,13 +83,41 @@ def setup():
FILEPATH Metadata directory location (default: cwd)\
"""

if flags("-h", "--help"):
raise SystemExit("\n".join([line.replace(" ", "", 1).rstrip() for
line in setup.__doc__.splitlines()]))
elif flags("-v", "--version"):
raise SystemExit(VERSION)
elif len(sys.argv) >= 2:
if len(sys.argv) >= 2:
for arg in sys.argv[1:]:
print(get_config(arg))
else:
print(get_config())


@help_and_version
def info():
"""py-info will print the most recent version of a package's metadata.
Usage:
py-info <package> [package] ...\
"""

env = pkg_resources.Environment()
separator = False
for arg in sys.argv[1:]:
pkg = env[pkg_resources.safe_name(arg)][0]
if not pkg:
print("The package {} was not found.".format(arg))
continue
elif pkg.PKG_INFO != "METADATA":
print("The package {} does not use metadata.".format(arg))
continue

# this is without a doubt the dumbest line of code I have written
# it's also the only way I could find to get the package's metadata
# don't look too close, we're dealing with an email message object
metadata = pkg._parsed_pkg_info.items()

print("{}{}".format(
"{}\n".format("-" * 40) if separator else "",
"\n".join([
"{}: {}".format(k, v) for k, v in metadata if v != "UNKNOWN"
]),
))
separator = True
50 changes: 50 additions & 0 deletions pypackage/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from .runner import UNITTEST_TEMPLATE
from .constants import META_NAME
from .constants import STRING_TYPE
from .constants import VERSION
from .constants import UNICODE


class UNDEF(object):
Expand Down Expand Up @@ -81,12 +83,16 @@ class Config(object):
("use_2to3", bool),
("convert_2to3_doctests", list),
("use_2to3_fixtures", list),
("extensions", {str: list}),
("metadata", object),
])
# our distinct keys, these do not get passed to setuptools/distutils
_PYPACKAGE_KEYS = OrderedDict([
("test_runner", str),
("tests_dir", str), # no function required for these two, they're
("runner_args", list), # only used in the enable_test_runner function
("source_label", str), # these two are in a draft pep 426
("source_url", str), # early implementation here
])

def __init__(self, **kwargs):
Expand Down Expand Up @@ -447,11 +453,55 @@ def _enable_pytest(self):
if hasattr(self, "tests_dir"):
self.runner_args.append(self.tests_dir)

def _enable_metadata_obj(self):
"""If source_label or source_url is used, build the metadata object."""

if any(getattr(self, k, None) for k in ("source_url", "source_label")):
undef = "UNKNOWN"

class Metadata(object):
"""Metadata object class built with self attributes."""

metadata_version = "2.0"
generator = "pypackage ({})".format(VERSION)
name = getattr(self, "name", undef)
version = getattr(self, "version", undef)
summary = getattr(self, "description", undef)
home_page = getattr(self, "url", undef)
url = home_page
author = getattr(self, "author", undef)
author_email = getattr(self, "author_email", undef)
license = getattr(self, "license", undef)
platforms = getattr(self, "platforms", undef)
platform = platforms # for PKG-INFO
keywords = getattr(self, "keywords", [])
source_label = getattr(self, "source_label", undef)
source_url = getattr(self, "source_url", undef)

def write_pkg_info(self, egg_info):
"""Include the future source keys in PKG-INFO."""

pkg_info = os.path.join(egg_info, "PKG-INFO")
info_attrs = ["name", "version", "summary", "home_page",
"author", "author_email", "license",
"description", "platform", "source_label",
"source_url"]
with io.open(pkg_info, "w", encoding="utf-8") as openinfo:
openinfo.write(UNICODE("Metadata-Version: 1.0\n"))
for attr in info_attrs:
openinfo.write(UNICODE("{}: {}\n".format(
attr.title().replace("_", "-"),
getattr(Metadata, attr),
)))

self.metadata = Metadata()

def _verify(self):
"""Ensures self attributes conform to their type declarations."""

self._enable_test_runner()
self._enable_long_description_read()
self._enable_metadata_obj()

for key, type_ in list(Config._KEYS.items()) + list(
Config._PYPACKAGE_KEYS.items()):
Expand Down
2 changes: 2 additions & 0 deletions pypackage/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
INPUT = input
STRING_TYPE = str
CHECKMARK = chr(0x2713)
UNICODE = str
else: # pragma: no cover
INPUT = raw_input # nopep8
STRING_TYPE = basestring # nopep8
CHECKMARK = unichr(0x2713) # nopep8
UNICODE = unicode # nopep8


VERSION = "{} {}".format(
Expand Down
4 changes: 3 additions & 1 deletion pypackage/guessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,9 @@ def _guess_at_things(config):
root = os.path.join(os.path.abspath(os.curdir), potential)
if os.path.isdir(root):
for file_ in os.listdir(root):
if os.access(os.path.join(root, file_), os.X_OK):
# windows does not have a firm grasp of executable files
if os.name == "nt" or \
os.access(os.path.join(root, file_), os.X_OK):
scripts.append(os.path.join(potential, file_))

if scripts:
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def long_description():
entry_points={"console_scripts": [
"py-build = pypackage.commands:build",
"py-develop = pypackage.commands:develop",
"py-info = pypackage.commands:info",
"py-install = pypackage.commands:install",
"py-setup = pypackage.commands:setup",
"py-test = pypackage.commands:run_tests",
Expand Down
3 changes: 2 additions & 1 deletion test/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import mock
import pytest

import pypackage
from pypackage import commands


Expand Down Expand Up @@ -80,7 +81,7 @@ def test_setup_entry__version(reset_sys_argv, flag):
with pytest.raises(SystemExit) as patched_exit:
commands.setup()

assert patched_exit.value.args[0] == commands.VERSION
assert patched_exit.value.args[0] == pypackage.VERSION


def test_setup_entry__location(reset_sys_argv):
Expand Down

0 comments on commit 7c0e177

Please sign in to comment.