Repository: pypa/setuptools

Issue:
Since setuptools 28.5.0, a "global-exclude .pyc" no longer matches .pyc files
Since commit bb45468d27615c2ce9f9c9757a367c44d6ee80d2, if a project's MANIFEST.in contains ``global-exclude .py[co]``, the archive generated with ``python setup.py sdist`` will still contain all ``.pyc`` / ``.pyo`` files.

The test below shows that this line worked in setuptools 28.4.0, and broke in setuptools 28.5.0.

I'm not sure whether this can be considered a bug: the previous implementation looked up patterns without any anchoring, whereas the new one forces users to explicitly include wildcards in their ``MANIFEST.in``.
However, I think this change should have been mentioned in the version Changelog.

```patch
diff --cc setuptools/tests/test_manifest.py
index 6360270,ef4beda..0000000
--- a/setuptools/tests/test_manifest.py
+++ b/setuptools/tests/test_manifest.py
@@@ -425,12 -464,22 +425,19 @@@ class TestFileListTest(TempDirTestCase)
          assert file_list.files == ['b.txt']
          self.assertWarnings()
  
+         file_list = FileList()
+         file_list.files = ['a.py', 'b.txt', l('d/c.pyc'), 'e.pyo']
+         file_list.process_template_line('global-exclude .py[co]')
+         file_list.sort()
+         assert file_list.files == ['a.py', 'b.txt']
+         self.assertNoWarnings()
+ 
          # recursive-include
  ```


Discussion:
Thanks @rbarrois for tracing the issue and providing a unit test.

I agree this looks like an unintentional regression. 

According to the changelog, #764 is implicated in this change. Since the unit tests didn't capture this behavior before, it's no surprise that it was missed during the re-write.

@timheap How difficult would it be to restore this expectation?

@jaraco It is simple to restore this behaviour for `global-include` and `global-exclude`. I've done so over in #853. Sorry for breaking your build, @rbarrois!

However, I will argue that the old behaviour is unexpected and not desired. In my opinion, a note in the Changelog about this breaking change should be included, rather than restoring the old behaviour. The new behaviour behaves much more like globbing does in other environments (e.g. a bash shell), and is less surprising. With the old behaviour, a pattern like `global-exclude bar.py` would exclude `bar.py`, but also surprisingly `foo_bar.py`, and there is no simple way of stopping this from happening. Under the new behaviour, `global-exclude bar.py` would only exclude `bar.py`, and not `foo_bar.py`. @rbarrois has already found the simple work around to restore the old behaviour by explicitly globbing the match like `global-exclude *.py[co]`.
@timheap thanks! I do agree with you: I wouldn't consider "properly anchoring patterns" as a bug, but rather as a proper bugfix. I'm totally in favor of keeping the fixed behavior, and simply documenting "a longstanding bug has been fixed in passing while cleaning up this code — please fix your manifests.".

Resolving patch:
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -457,7 +457,7 @@ def global_include(self, pattern):
         """
         if self.allfiles is None:
             self.findall()
-        match = translate_pattern(os.path.join('**', '*' + pattern))
+        match = translate_pattern(os.path.join('**', pattern))
         found = [f for f in self.allfiles if match.match(f)]
         self.extend(found)
         return bool(found)
@@ -466,7 +466,7 @@ def global_exclude(self, pattern):
         """
         Exclude all files anywhere that match the pattern.
         """
-        match = translate_pattern(os.path.join('**', '*' + pattern))
+        match = translate_pattern(os.path.join('**', pattern))
         return self._remove_files(match.match)
 
     def append(self, item):


Test patch:
diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py
--- a/setuptools/tests/test_manifest.py
+++ b/setuptools/tests/test_manifest.py
@@ -449,11 +449,6 @@ def test_global_include(self):
         assert file_list.files == ['a.py', l('d/c.py')]
         self.assertWarnings()
 
-        file_list.process_template_line('global-include .txt')
-        file_list.sort()
-        assert file_list.files == ['a.py', 'b.txt', l('d/c.py')]
-        self.assertNoWarnings()
-
     def test_global_exclude(self):
         l = make_local_path
         # global-exclude
@@ -470,13 +465,6 @@ def test_global_exclude(self):
         assert file_list.files == ['b.txt']
         self.assertWarnings()
 
-        file_list = FileList()
-        file_list.files = ['a.py', 'b.txt', l('d/c.pyc'), 'e.pyo']
-        file_list.process_template_line('global-exclude .py[co]')
-        file_list.sort()
-        assert file_list.files == ['a.py', 'b.txt']
-        self.assertNoWarnings()
-
     def test_recursive_include(self):
         l = make_local_path
         # recursive-include