Skip to content

Commit 60503d9

Browse files
committed
Add support for cleaning up after sdist.
1 parent bad9298 commit 60503d9

File tree

4 files changed

+147
-5
lines changed

4 files changed

+147
-5
lines changed

README.rst

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,50 @@ keeping targets such as *clean*, *dist-clean*, and *maintainer-clean*.
2525
This extension is inspired by the same desire for a clean working
2626
environment.
2727

28-
Ok... Where?
29-
------------
28+
Installation
29+
~~~~~~~~~~~~
30+
The ``setuptools`` package contains a number of interesting ways in which
31+
it can be extended. If you develop Python packages, then you can include
32+
extension packages using the ``setup_requires`` and ``cmdclass`` keyword
33+
parameters to the ``setup`` function call. This is a little more
34+
difficult than it should be since the ``setupext`` package needs to be
35+
imported into *setup.py* so that it can be passed as a keyword parameter
36+
**before** it is downloaded. The easiest way to do this is to catch the
37+
``ImportError`` that happens if it is not already downloaded::
38+
39+
import setuptools
40+
try:
41+
from setupext import janitor
42+
CleanCommand = janitor.CleanCommand
43+
except ImportError:
44+
CleanCommand = None
45+
46+
cmd_classes = {}
47+
if CleanCommand is not None:
48+
cmd_classes['clean'] = CleanCommand
49+
50+
setup(
51+
# normal parameters
52+
setup_requires=['setupext.janitor'],
53+
cmdclass=cmd_classes,
54+
)
55+
56+
You can use a different approach if you are simply a developer that wants
57+
to have this functionality available for your own use, then you can install
58+
it into your working environment. This package installs itself into the
59+
environment as a `distutils extension`_ so that it is available to any
60+
*setup.py* script as if by magic.
61+
62+
Usage
63+
~~~~~
64+
Once the extension is installed, the ``clean`` command will accept a
65+
few new command line parameters.
66+
67+
``setup.py clean --dist``
68+
Removes directories that the various *dist* commands produce.
69+
70+
Where can I get this extension from?
71+
------------------------------------
3072
+---------------+-----------------------------------------------------+
3173
| Source | https://github.com/dave-shawley/setupext-janitor |
3274
+---------------+-----------------------------------------------------+
@@ -39,7 +81,10 @@ Ok... Where?
3981
| Issues | https://github.com/dave-shawley/setupext-janitor |
4082
+---------------+-----------------------------------------------------+
4183

84+
.. _distutils extension: https://pythonhosted.org/setuptools/setuptools.html
85+
#extending-and-reusing-setuptools
4286
.. _setuptools: https://pythonhosted.org/setuptools/
87+
4388
.. |Version| image:: https://badge.fury.io/py/setupext-janitor.svg
4489
:target: https://badge.fury.io/
4590
.. |Downloads| image:: https://pypip.in/d/setupext-janitor/badge.svg?
@@ -48,4 +93,3 @@ Ok... Where?
4893
:target: https://travis-ci.org/dave-shawley/setupext-janitor
4994
.. |License| image:: https://pypip.in/license/dave-shawley/badge.svg?
5095
:target: https://setupext-dave-shawley.readthedocs.org/
51-

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
],
4242
entry_points={
4343
'distutils.commands': [
44+
'clean = setupext.janitor:CleanCommand',
4445
],
4546
},
4647
)

setupext/janitor/__init__.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
from distutils import log
12
from distutils.command.clean import clean as _CleanCommand
3+
import shutil
24

35

46
version_info = (0, 0, 0)
@@ -33,7 +35,28 @@ class CleanCommand(_CleanCommand):
3335
"""
3436

3537
# See _set_options for `user_options`
36-
pass
38+
39+
def __init__(self, *args, **kwargs):
40+
_CleanCommand.__init__(self, *args, **kwargs)
41+
self.dist = None
42+
43+
def initialize_options(self):
44+
_CleanCommand.initialize_options(self)
45+
self.dist = False
46+
47+
def run(self):
48+
_CleanCommand.run(self)
49+
dist_dirs = set()
50+
for cmd_name in self.distribution.commands:
51+
if cmd_name == 'sdist':
52+
command = self.distribution.get_command_obj(cmd_name)
53+
command.ensure_finalized()
54+
if getattr(command, 'dist_dir', None):
55+
dist_dirs.add(command.dist_dir)
56+
57+
for dir_name in dist_dirs:
58+
self.announce('removing {0}'.format(dir_name), level=log.DEBUG)
59+
shutil.rmtree(dir_name, ignore_errors=True)
3760

3861

3962
def _set_options():
@@ -56,5 +79,7 @@ def _set_options():
5679
CleanCommand.user_options.extend([
5780
('dist', 'd', 'remove distribution directory'),
5881
])
82+
CleanCommand.boolean_options = _CleanCommand.boolean_options[:]
83+
CleanCommand.boolean_options.append('dist')
5984

6085
_set_options()

tests.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,59 @@
1+
from distutils import core, dist
12
from distutils.command import clean
3+
import atexit
4+
import os.path
5+
import shutil
26
import sys
7+
import tempfile
38

49
if sys.version_info >= (2, 7):
510
import unittest
6-
else:
11+
else: # noinspection PyPackageRequirements,PyUnresolvedReferences
712
import unittest2 as unittest
813

914
from setupext import janitor
1015

1116

17+
def run_setup(*command_line):
18+
"""
19+
Run the setup command with `command_line`.
20+
21+
:param command_line: the command line arguments to pass
22+
as the simulated command line
23+
24+
This function runs :func:`distutils.core.setup` after it
25+
configures an environment that mimics passing the specified
26+
command line arguments. The ``distutils`` internals are
27+
replaced with a :class:`~distutils.dist.Distribution`
28+
instance that will only execute the clean command. Other
29+
commands can be passed freely to simulate command line usage
30+
patterns.
31+
32+
"""
33+
class FakeDistribution(dist.Distribution):
34+
35+
def __init__(self, *args, **kwargs):
36+
"""Enable verbose output to make tests easier to debug."""
37+
dist.Distribution.__init__(self, *args, **kwargs)
38+
self.verbose = 3
39+
40+
def run_command(self, command):
41+
"""Only run the clean command."""
42+
if command == 'clean':
43+
dist.Distribution.run_command(self, command)
44+
45+
def parse_config_files(self, filenames=None):
46+
"""Skip processing of configuration files."""
47+
pass
48+
49+
core.setup(
50+
distclass=FakeDistribution,
51+
script_name='testsetup.py',
52+
script_args=command_line,
53+
cmdclass={'clean': janitor.CleanCommand},
54+
)
55+
56+
1257
class CommandOptionTests(unittest.TestCase):
1358

1459
def test_that_distutils_options_are_present(self):
@@ -24,3 +69,30 @@ def test_that_janitor_defines_dist_command(self):
2469
self.assertIn(
2570
('dist', 'd', 'remove distribution directory'),
2671
janitor.CleanCommand.user_options)
72+
73+
74+
class DirectoryCleanupTests(unittest.TestCase):
75+
temp_dir = tempfile.mkdtemp()
76+
77+
@classmethod
78+
def setUpClass(cls):
79+
super(DirectoryCleanupTests, cls).setUpClass()
80+
atexit.register(shutil.rmtree, cls.temp_dir)
81+
82+
@classmethod
83+
def create_directory(cls, dir_name):
84+
full_path = os.path.join(cls.temp_dir, dir_name)
85+
os.mkdir(full_path)
86+
return full_path
87+
88+
def assert_path_does_not_exist(self, full_path):
89+
if os.path.exists(full_path):
90+
raise AssertionError('{0} should not exist'.format(full_path))
91+
92+
def test_that_dist_directory_is_removed_for_sdist(self):
93+
dist_dir = self.create_directory('dist-dir')
94+
run_setup(
95+
'sdist', '--dist-dir={0}'.format(dist_dir),
96+
'clean', '--dist',
97+
)
98+
self.assert_path_does_not_exist(dist_dir)

0 commit comments

Comments
 (0)