python (3.11.7)
       1  from distutils import log
       2  import distutils.command.sdist as orig
       3  import os
       4  import sys
       5  import io
       6  import contextlib
       7  from itertools import chain
       8  
       9  from .py36compat import sdist_add_defaults
      10  
      11  from .._importlib import metadata
      12  from .build import _ORIGINAL_SUBCOMMANDS
      13  
      14  _default_revctrl = list
      15  
      16  
      17  def walk_revctrl(dirname=''):
      18      """Find all files under revision control"""
      19      for ep in metadata.entry_points(group='setuptools.file_finders'):
      20          for item in ep.load()(dirname):
      21              yield item
      22  
      23  
      24  class ESC[4;38;5;81msdist(ESC[4;38;5;149msdist_add_defaults, ESC[4;38;5;149morigESC[4;38;5;149m.ESC[4;38;5;149msdist):
      25      """Smart sdist that finds anything supported by revision control"""
      26  
      27      user_options = [
      28          ('formats=', None,
      29           "formats for source distribution (comma-separated list)"),
      30          ('keep-temp', 'k',
      31           "keep the distribution tree around after creating " +
      32           "archive file(s)"),
      33          ('dist-dir=', 'd',
      34           "directory to put the source distribution archive(s) in "
      35           "[default: dist]"),
      36          ('owner=', 'u',
      37           "Owner name used when creating a tar file [default: current user]"),
      38          ('group=', 'g',
      39           "Group name used when creating a tar file [default: current group]"),
      40      ]
      41  
      42      negative_opt = {}
      43  
      44      README_EXTENSIONS = ['', '.rst', '.txt', '.md']
      45      READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS)
      46  
      47      def run(self):
      48          self.run_command('egg_info')
      49          ei_cmd = self.get_finalized_command('egg_info')
      50          self.filelist = ei_cmd.filelist
      51          self.filelist.append(os.path.join(ei_cmd.egg_info, 'SOURCES.txt'))
      52          self.check_readme()
      53  
      54          # Run sub commands
      55          for cmd_name in self.get_sub_commands():
      56              self.run_command(cmd_name)
      57  
      58          self.make_distribution()
      59  
      60          dist_files = getattr(self.distribution, 'dist_files', [])
      61          for file in self.archive_files:
      62              data = ('sdist', '', file)
      63              if data not in dist_files:
      64                  dist_files.append(data)
      65  
      66      def initialize_options(self):
      67          orig.sdist.initialize_options(self)
      68  
      69          self._default_to_gztar()
      70  
      71      def _default_to_gztar(self):
      72          # only needed on Python prior to 3.6.
      73          if sys.version_info >= (3, 6, 0, 'beta', 1):
      74              return
      75          self.formats = ['gztar']
      76  
      77      def make_distribution(self):
      78          """
      79          Workaround for #516
      80          """
      81          with self._remove_os_link():
      82              orig.sdist.make_distribution(self)
      83  
      84      @staticmethod
      85      @contextlib.contextmanager
      86      def _remove_os_link():
      87          """
      88          In a context, remove and restore os.link if it exists
      89          """
      90  
      91          class ESC[4;38;5;81mNoValue:
      92              pass
      93  
      94          orig_val = getattr(os, 'link', NoValue)
      95          try:
      96              del os.link
      97          except Exception:
      98              pass
      99          try:
     100              yield
     101          finally:
     102              if orig_val is not NoValue:
     103                  setattr(os, 'link', orig_val)
     104  
     105      def add_defaults(self):
     106          super().add_defaults()
     107          self._add_defaults_build_sub_commands()
     108  
     109      def _add_defaults_optional(self):
     110          super()._add_defaults_optional()
     111          if os.path.isfile('pyproject.toml'):
     112              self.filelist.append('pyproject.toml')
     113  
     114      def _add_defaults_python(self):
     115          """getting python files"""
     116          if self.distribution.has_pure_modules():
     117              build_py = self.get_finalized_command('build_py')
     118              self.filelist.extend(build_py.get_source_files())
     119              self._add_data_files(self._safe_data_files(build_py))
     120  
     121      def _add_defaults_build_sub_commands(self):
     122          build = self.get_finalized_command("build")
     123          missing_cmds = set(build.get_sub_commands()) - _ORIGINAL_SUBCOMMANDS
     124          # ^-- the original built-in sub-commands are already handled by default.
     125          cmds = (self.get_finalized_command(c) for c in missing_cmds)
     126          files = (c.get_source_files() for c in cmds if hasattr(c, "get_source_files"))
     127          self.filelist.extend(chain.from_iterable(files))
     128  
     129      def _safe_data_files(self, build_py):
     130          """
     131          Since the ``sdist`` class is also used to compute the MANIFEST
     132          (via :obj:`setuptools.command.egg_info.manifest_maker`),
     133          there might be recursion problems when trying to obtain the list of
     134          data_files and ``include_package_data=True`` (which in turn depends on
     135          the files included in the MANIFEST).
     136  
     137          To avoid that, ``manifest_maker`` should be able to overwrite this
     138          method and avoid recursive attempts to build/analyze the MANIFEST.
     139          """
     140          return build_py.data_files
     141  
     142      def _add_data_files(self, data_files):
     143          """
     144          Add data files as found in build_py.data_files.
     145          """
     146          self.filelist.extend(
     147              os.path.join(src_dir, name)
     148              for _, src_dir, _, filenames in data_files
     149              for name in filenames
     150          )
     151  
     152      def _add_defaults_data_files(self):
     153          try:
     154              super()._add_defaults_data_files()
     155          except TypeError:
     156              log.warn("data_files contains unexpected objects")
     157  
     158      def check_readme(self):
     159          for f in self.READMES:
     160              if os.path.exists(f):
     161                  return
     162          else:
     163              self.warn(
     164                  "standard file not found: should have one of " +
     165                  ', '.join(self.READMES)
     166              )
     167  
     168      def make_release_tree(self, base_dir, files):
     169          orig.sdist.make_release_tree(self, base_dir, files)
     170  
     171          # Save any egg_info command line options used to create this sdist
     172          dest = os.path.join(base_dir, 'setup.cfg')
     173          if hasattr(os, 'link') and os.path.exists(dest):
     174              # unlink and re-copy, since it might be hard-linked, and
     175              # we don't want to change the source version
     176              os.unlink(dest)
     177              self.copy_file('setup.cfg', dest)
     178  
     179          self.get_finalized_command('egg_info').save_version_info(dest)
     180  
     181      def _manifest_is_not_generated(self):
     182          # check for special comment used in 2.7.1 and higher
     183          if not os.path.isfile(self.manifest):
     184              return False
     185  
     186          with io.open(self.manifest, 'rb') as fp:
     187              first_line = fp.readline()
     188          return (first_line !=
     189                  '# file GENERATED by distutils, do NOT edit\n'.encode())
     190  
     191      def read_manifest(self):
     192          """Read the manifest file (named by 'self.manifest') and use it to
     193          fill in 'self.filelist', the list of files to include in the source
     194          distribution.
     195          """
     196          log.info("reading manifest file '%s'", self.manifest)
     197          manifest = open(self.manifest, 'rb')
     198          for line in manifest:
     199              # The manifest must contain UTF-8. See #303.
     200              try:
     201                  line = line.decode('UTF-8')
     202              except UnicodeDecodeError:
     203                  log.warn("%r not UTF-8 decodable -- skipping" % line)
     204                  continue
     205              # ignore comments and blank lines
     206              line = line.strip()
     207              if line.startswith('#') or not line:
     208                  continue
     209              self.filelist.append(line)
     210          manifest.close()