Repository: pybuilder/pybuilder
Issue: [distutils-plugin] Should package modules also, not just packages

Currently, as @esc and @ollihoo found out, the distutils plugin will only package python sources if there are in a package, e.g.

```
--src
-------main
----------------python
-------------------------------foo
---------------------------------------__init__.py
---------------------------------------something.py
```

But if there are only modules, e.G.

```
--src
-------main
----------------python
-------------------------------foo.py
-------------------------------bar.py
```

then pybuilder will create an "empty" source distribution.
The reason is that modules must be added to setup.py with `py_modules`, something the distutils-plugin currently does not do at all.


Changed files:
src/unittest/python/plugins/python/distutils_plugin_tests.py, src/integrationtest/python/should_write_setup_file_tests.py, src/main/python/pybuilder/plugins/python/distutils_plugin.py, src/unittest/python/plugins/python/core_plugin_tests.py, src/main/python/pybuilder/plugins/python/core_plugin.py

diff --git a/src/integrationtest/python/should_write_setup_file_tests.py b/src/integrationtest/python/should_write_setup_file_tests.py
index 242daff..c0f2c1a 100644
--- a/src/integrationtest/python/should_write_setup_file_tests.py
+++ b/src/integrationtest/python/should_write_setup_file_tests.py
@@ -39,6 +39,7 @@ def init (project):
     project.build_depends_on("eggy")
 """)
         self.create_directory("src/main/python/spam")
+        self.write_file("src/main/python/standalone_module.py")
         self.write_file("src/main/python/spam/__init__.py", "")
         self.write_file("src/main/python/spam/eggs.py", """
 def spam ():
@@ -50,6 +51,7 @@ def spam ():
 
         self.assert_directory_exists("target/dist/integration-test-1.0-SNAPSHOT")
         self.assert_directory_exists("target/dist/integration-test-1.0-SNAPSHOT/spam")
+        self.assert_file_exists("target/dist/integration-test-1.0-SNAPSHOT/standalone_module.py")
         self.assert_file_empty("target/dist/integration-test-1.0-SNAPSHOT/spam/__init__.py")
         self.assert_file_content("target/dist/integration-test-1.0-SNAPSHOT/spam/eggs.py", """
 def spam ():
@@ -76,6 +78,7 @@ if __name__ == '__main__':
           url = '',
           scripts = [],
           packages = ['spam'],
+          py_modules = ['standalone_module'],
           classifiers = ['Development Status :: 3 - Alpha', 'Programming Language :: Python'],
              #  data files
              # package data
diff --git a/src/main/python/pybuilder/plugins/python/core_plugin.py b/src/main/python/pybuilder/plugins/python/core_plugin.py
index fbe172d..98c5c8b 100644
--- a/src/main/python/pybuilder/plugins/python/core_plugin.py
+++ b/src/main/python/pybuilder/plugins/python/core_plugin.py
@@ -48,7 +48,15 @@ def init_python_directories(project):
                     package = package[1:].replace(os.sep, ".")
                     yield package
 
+    def list_modules():
+        source_path = project.expand_path("$dir_source_main_python")
+        for potential_module_file in os.listdir(source_path):
+            potential_module_path = os.path.join(source_path, potential_module_file)
+            if os.path.isfile(potential_module_path) and potential_module_file.endswith(".py"):
+                yield potential_module_file[:-len(".py")]
+
     project.list_packages = list_packages
+    project.list_modules = list_modules
 
     def list_scripts():
         scripts_dir = project.expand_path("$dir_source_main_scripts")
diff --git a/src/main/python/pybuilder/plugins/python/distutils_plugin.py b/src/main/python/pybuilder/plugins/python/distutils_plugin.py
index b75231b..e1fac7e 100644
--- a/src/main/python/pybuilder/plugins/python/distutils_plugin.py
+++ b/src/main/python/pybuilder/plugins/python/distutils_plugin.py
@@ -49,6 +49,7 @@ if __name__ == '__main__':
           url = '$url',
           scripts = $scripts,
           packages = $packages,
+          py_modules = $modules,
           classifiers = $classifiers,
           $data_files   #  data files
           $package_data   # package data
@@ -105,6 +106,7 @@ def render_setup_script(project):
         "url": default(project.url),
         "scripts": build_scripts_string(project),
         "packages": str([package for package in project.list_packages()]),
+        "modules": str([module for module in project.list_modules()]),
         "classifiers": project.get_property("distutils_classifiers"),
         "data_files": build_data_files_string(project),
         "package_data": build_package_data_string(project),
diff --git a/src/unittest/python/plugins/python/core_plugin_tests.py b/src/unittest/python/plugins/python/core_plugin_tests.py
index 0b98f3c..c491a42 100644
--- a/src/unittest/python/plugins/python/core_plugin_tests.py
+++ b/src/unittest/python/plugins/python/core_plugin_tests.py
@@ -15,20 +15,36 @@
 #  limitations under the License.
 import unittest
 
+from mock import patch
+
 from pybuilder.plugins.python.core_plugin import init_python_directories
 from pybuilder.plugins.python.core_plugin import (DISTRIBUTION_PROPERTY,
                                                   PYTHON_SOURCES_PROPERTY,
                                                   SCRIPTS_SOURCES_PROPERTY,
                                                   SCRIPTS_TARGET_PROPERTY)
-
 from pybuilder.core import Project
 
 
 class InitPythonDirectoriesTest (unittest.TestCase):
 
+    def greedy(self, generator):
+        return [element for element in generator]
+
     def setUp(self):
         self.project = Project(".")
 
+    @patch("pybuilder.plugins.python.core_plugin.os.listdir")
+    @patch("pybuilder.plugins.python.core_plugin.os.path.isfile")
+    def test_should_set_list_modules_function_with_project_modules(self, _, source_listdir):
+        source_listdir.return_value = ["foo.py", "bar.py", "some-package"]
+
+        init_python_directories(self.project)
+
+        self.assertEquals(
+            ['foo', 'bar'],
+            self.greedy(self.project.list_modules())
+        )
+
     def test_should_set_python_sources_property(self):
         init_python_directories(self.project)
         self.assertEquals(
diff --git a/src/unittest/python/plugins/python/distutils_plugin_tests.py b/src/unittest/python/plugins/python/distutils_plugin_tests.py
index 32811ba..1f4df11 100644
--- a/src/unittest/python/plugins/python/distutils_plugin_tests.py
+++ b/src/unittest/python/plugins/python/distutils_plugin_tests.py
@@ -212,6 +212,7 @@ if __name__ == '__main__':
           url = 'http://github.com/pybuilder/pybuilder',
           scripts = ['spam', 'eggs'],
           packages = ['spam', 'eggs'],
+          py_modules = ['spam', 'eggs'],
           classifiers = ['Development Status :: 5 - Beta', 'Environment :: Console'],
           data_files = [('dir', ['file1', 'file2'])],   #  data files
           package_data = {'spam': ['eggs']},   # package data
@@ -255,6 +256,7 @@ def create_project():
 
     project.list_scripts = return_dummy_list
     project.list_packages = return_dummy_list
+    project.list_modules = return_dummy_list
 
     project.set_property("distutils_classifiers", [
                          "Development Status :: 5 - Beta", "Environment :: Console"])