def test_exported_thrift_issues_2005(self): # Issue #2005 highlighted the fact the PythonThriftBuilder was building both a given # PythonThriftLibrary's thrift files as well as its transitive dependencies thrift files. # We test here to ensure that independently published PythonThriftLibraries each only own their # own thrift stubs and the proper dependency links exist between the distributions. self.create_file(relpath='src/thrift/exported/exported.thrift', contents=dedent(""" namespace py exported const set<string> VALID_IDENTIFIERS = ["Hello", "World", "!"] """)) target1 = self.make_target(spec='src/thrift/exported', target_type=PythonThriftLibrary, sources=['exported.thrift'], provides=PythonArtifact(name='test.exported', version='0.0.0')) self.create_file(relpath='src/thrift/exported_dependee/exported_dependee.thrift', contents=dedent(""" namespace py exported_dependee include "exported/exported.thrift" const set<string> ALIASED_IDENTIFIERS = exported.VALID_IDENTIFIERS """)) target2 = self.make_target(spec='src/thrift/exported_dependee', target_type=PythonThriftLibrary, sources=['exported_dependee.thrift'], dependencies=[target1], provides=PythonArtifact(name='test.exported_dependee', version='0.0.0')) with self.run_execute(target2, recursive=True) as created: self.assertEqual({target2, target1}, set(created.keys())) with self.extracted_sdist(sdist=created[target1], expected_prefix='test.exported-0.0.0') as (py_files, path): self.assertEqual({path('setup.py'), path('src/__init__.py'), path('src/exported/__init__.py'), path('src/exported/constants.py'), path('src/exported/ttypes.py')}, py_files) self.assertFalse(os.path.exists(path('src/test.exported.egg-info/requires.txt'))) with self.extracted_sdist(sdist=created[target2], expected_prefix='test.exported_dependee-0.0.0') as (py_files, path): self.assertEqual({path('setup.py'), path('src/__init__.py'), path('src/exported_dependee/__init__.py'), path('src/exported_dependee/constants.py'), path('src/exported_dependee/ttypes.py')}, py_files) requirements = path('src/test.exported_dependee.egg-info/requires.txt') self.assertTrue(os.path.exists(requirements)) with open(requirements) as fp: self.assertEqual('test.exported==0.0.0', fp.read().strip())
def test_exported_antlr(self): SourceRoot.register('src/antlr', PythonThriftLibrary) self.create_file(relpath='src/antlr/exported/exported.g', contents=dedent(""" grammar exported; options { language = Python; } WORD: ('a'..'z'|'A'..'Z'|'0'..'9'|'-'|'_')+; static: WORD; """)) target = self.make_target(spec='src/antlr/exported', target_type=PythonAntlrLibrary, antlr_version='3.1.3', sources=['exported.g'], module='exported', provides=PythonArtifact(name='test.exported', version='0.0.0.')) # TODO(John Sirois): Find a way to get easy access to pants option defaults. self.set_options_for_scope( 'ivy', ivy_profile='2.4.0', ivy_settings=None, cache_dir=os.path.expanduser('~/.ivy2/pants'), http_proxy=None, https_proxy=None) with self.run_execute(target) as created: self.assertEqual([target], created)
def test_exported_antlr(self): self.create_file(relpath='src/antlr/exported/exported.g', contents=dedent(""" grammar exported; options { language = Python; } WORD: ('a'..'z'|'A'..'Z'|'0'..'9'|'-'|'_')+; static: WORD; """)) target = self.make_target(spec='src/antlr/exported', target_type=PythonAntlrLibrary, antlr_version='3.1.3', sources=['exported.g'], module='exported', provides=PythonArtifact(name='test.exported', version='0.0.0')) # TODO(John Sirois): This hacks around a direct but undeclared dependency # `pants.java.distribution.distribution.Distribution` gained in # https://rbcommons.com/s/twitter/r/2657 # Remove this once proper Subsystem dependency chains are re-established. with subsystem_instance(JVM): with self.run_execute(target) as created: self.assertEqual([target], created.keys())
def test_validation(self): internal_repo = Repository(url=None, push_db_basedir=None) # Adding a JVM Artifact as a provides on a PythonTarget doesn't make a lot of sense. # This test sets up that very scenario, and verifies that pants throws a # TargetDefinitionException. with self.assertRaises(TargetDefinitionException): self.make_target(target_type=PythonTarget, spec=":one", provides=Artifact(org='com.twitter', name='one-jar', repo=internal_repo)) spec = "//:test-with-PythonArtifact" pa = PythonArtifact(name='foo', version='1.0', description='foo') # This test verifies that adding a 'setup_py' provides to a PythonTarget is okay. pt_with_artifact = self.make_target(spec=spec, target_type=PythonTarget, provides=pa) self.assertEqual(pt_with_artifact.address.spec, spec) spec = "//:test-with-none" # This test verifies that having no provides is okay. pt_no_artifact = self.make_target(spec=spec, target_type=PythonTarget, provides=None) self.assertEqual(pt_no_artifact.address.spec, spec)
def test_binary_target_injected_into_minified_dependencies(self): foo_bin_dep = self.make_target( spec=':foo_bin_dep', target_type=PythonLibrary, ) foo_bin = self.make_target(spec=':foo_bin', target_type=PythonBinary, entry_point='foo.bin.foo', dependencies=[ foo_bin_dep, ]) foo = self.make_target(spec=':foo', target_type=PythonLibrary, provides=PythonArtifact( name='foo', version='0.0.0', ).with_binaries(foo_binary=':foo_bin', )) self.assertEqual(PythonSetup.minified_dependencies(foo), OrderedSet([foo_bin, foo_bin_dep])) entry_points = dict(PythonSetup.iter_entry_points(foo)) self.assertEqual(entry_points, {'foo_binary': 'foo.bin.foo'}) with self.run_execute(foo, recursive=False) as setup_py_command: setup_py_command.run_one.assert_called_with(foo) with self.run_execute(foo, recursive=True) as setup_py_command: setup_py_command.run_one.assert_called_with(foo)
def test_list_provides() -> None: sample_artifact = PythonArtifact(name="project.demo", version="0.0.0.1") targets = [ MockTarget({ProvidesField.alias: sample_artifact}, address=Address.parse(":provided")), MockTarget({}, address=Address.parse(":not_provided")), ] stdout, _ = run_goal(targets, show_provides=True) assert stdout.strip() == f"//:provided {sample_artifact}"
def test_binary_target_injected_into_minified_dependencies_with_provider(self): bar_bin_dep = self.make_target( spec = ':bar_bin_dep', target_type = PythonLibrary, provides = PythonArtifact( name = 'bar_bin_dep', version = '0.0.0', ) ) bar_bin = self.make_target( spec = ':bar_bin', target_type = PythonBinary, entry_point = 'bar.bin.bar', dependencies = [ bar_bin_dep, ], ) bar = self.make_target( spec = ':bar', target_type = PythonLibrary, provides = PythonArtifact( name = 'bar', version = '0.0.0', ).with_binaries( bar_binary = ':bar_bin' ) ) # TODO(pl): Why is this set ordered? Does the order actually matter? assert PythonSetup.minified_dependencies(bar) == OrderedSet([bar_bin, bar_bin_dep]) assert PythonSetup.install_requires(bar) == set(['bar_bin_dep==0.0.0']) entry_points = dict(PythonSetup.iter_entry_points(bar)) assert entry_points == {'bar_binary': 'bar.bin.bar'} with self.run_execute(bar, recursive=False) as setup_py_command: setup_py_command.run_one.assert_called_with(bar) with self.run_execute(bar, recursive=True) as setup_py_command: setup_py_command.run_one.assert_has_calls([ call(bar), call(bar_bin_dep) ], any_order=True)
def test_exported_thrift(self): self.create_file(relpath='src/thrift/exported/exported.thrift', contents=dedent(""" namespace py pants.constants_only const set<string> VALID_IDENTIFIERS = ["Hello", "World", "!"] """)) target = self.make_target(spec='src/thrift/exported', target_type=PythonThriftLibrary, sources=['exported.thrift'], provides=PythonArtifact(name='test.exported', version='0.0.0')) with self.run_execute(target) as created: self.assertEqual([target], created.keys())
def pants_setup_py(name: str, description: str, additional_classifiers: Optional[List[str]] = None, **kwargs) -> PythonArtifact: """Creates the setup_py for a Pants artifact. :param name: The name of the package. :param description: A brief description of what the package provides. :param additional_classifiers: Any additional trove classifiers that apply to the package, see: https://pypi.org/pypi?%3Aaction=list_classifiers :param kwargs: Any additional keyword arguments to be passed to `setuptools.setup <https://pythonhosted.org/setuptools/setuptools.html>`_. :returns: A setup_py suitable for building and publishing Pants components. """ if not name.startswith("pantsbuild.pants"): raise ValueError( f"Pants distribution package names must start with 'pantsbuild.pants', given {name}" ) standard_classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", # We know for a fact these OSs work but, for example, know Windows # does not work yet. Take the conservative approach and only list OSs # we know pants works with for now. "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Topic :: Software Development :: Build Tools", ] classifiers = FrozenOrderedSet(standard_classifiers + (additional_classifiers or [])) notes = PantsReleases.global_instance().notes_for_version(PANTS_SEMVER) return PythonArtifact( name=name, version=VERSION, description=description, long_description=Path("src/python/pants/ABOUT.rst").read_text() + notes, long_description_content_type="text/x-rst", url="https://github.com/pantsbuild/pants", project_urls={ "Documentation": "https://www.pantsbuild.org/", "Source": "https://github.com/pantsbuild/pants", "Tracker": "https://github.com/pantsbuild/pants/issues", }, license="Apache License, Version 2.0", zip_safe=True, classifiers=list(classifiers), **kwargs, )
def create_dependencies(self, depmap): target_map = {} for name, deps in depmap.items(): target_map[name] = self.make_target( spec=name, target_type=PythonLibrary, provides=PythonArtifact(name=name, version='0.0.0')) for name, deps in depmap.items(): target = target_map[name] dep_targets = [target_map[name] for name in deps] for dep in dep_targets: self.build_graph.inject_dependency(target.address, dep.address) return target_map
def pants_setup_py(name, description, additional_classifiers=None, **kwargs): """Creates the setup_py for a pants artifact. :param str name: The name of the package. :param str description: A brief description of what the package provides. :param list additional_classifiers: Any additional trove classifiers that apply to the package, see: https://pypi.org/pypi?%3Aaction=list_classifiers :param kwargs: Any additional keyword arguments to be passed to `setuptools.setup <https://pythonhosted.org/setuptools/setuptools.html>`_. :returns: A setup_py suitable for building and publishing pants components. """ if not name.startswith('pantsbuild.pants'): raise ValueError( f"Pants distribution package names must start with 'pantsbuild.pants', given {name}" ) standard_classifiers = [ 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', # We know for a fact these OSs work but, for example, know Windows # does not work yet. Take the conservative approach and only list OSs # we know pants works with for now. 'Operating System :: MacOS :: MacOS X', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', 'Topic :: Software Development :: Build Tools' ] classifiers = OrderedSet(standard_classifiers + (additional_classifiers or [])) notes = PantsReleases.global_instance().notes_for_version(PANTS_SEMVER) return PythonArtifact( name=name, version=VERSION, description=description, long_description=Path('src/python/pants/ABOUT.rst').read_text() + notes, url='https://github.com/pantsbuild/pants', project_urls={ 'Documentation': 'https://www.pantsbuild.org/', 'Source': 'https://github.com/pantsbuild/pants', 'Tracker': 'https://github.com/pantsbuild/pants/issues', }, license='Apache License, Version 2.0', zip_safe=True, classifiers=list(classifiers), **kwargs)
def pants_setup_py(name, description, additional_classifiers=None, **kwargs): """Creates the setup_py for a pants artifact. :param str name: The name of the package. :param str description: A brief description of what the package provides. :param list additional_classifiers: Any additional trove classifiers that apply to the package, see: https://pypi.python.org/pypi?%3Aaction=list_classifiers :param kwargs: Any additional keyword arguments to be passed to `setuptools.setup <https://pythonhosted.org/setuptools/setuptools.html>`_. :returns: A setup_py suitable for building and publishing pants components. """ if not name.startswith('pantsbuild.pants'): raise ValueError( "Pants distribution package names must start with 'pantsbuild.pants', " "given {}".format(name)) standard_classifiers = [ 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', # We know for a fact these OSs work but, for example, know Windows # does not work yet. Take the conservative approach and only list OSs # we know pants works with for now. 'Operating System :: MacOS :: MacOS X', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', 'Topic :: Software Development :: Build Tools' ] classifiers = OrderedSet(standard_classifiers + (additional_classifiers or [])) def _read_contents(path): with open(os.path.join(get_buildroot(), path), 'rb') as fp: return fp.read() return PythonArtifact( name=name, version=pants_version(), description=description, long_description=(_read_contents('src/python/pants/ABOUT.rst') + _read_contents('src/python/pants/CHANGELOG.rst')), url='https://github.com/pantsbuild/pants', license='Apache License, Version 2.0', zip_safe=True, classifiers=list(classifiers), **kwargs)
def test_binary_cycle(self): foo = self.make_target(spec=':foo', target_type=PythonLibrary, provides=PythonArtifact( name='foo', version='0.0.0', ).with_binaries(foo_binary=':foo_bin', )) foo_bin = self.make_target( spec=':foo_bin', target_type=PythonBinary, entry_point='foo.bin.foo', dependencies=[ foo, ], ) with self.assertRaises(TargetDefinitionException): PythonSetup.minified_dependencies(foo)
def test_exported_thrift(self): SourceRoot.register('src/thrift', PythonThriftLibrary) self.create_file(relpath='src/thrift/exported/exported.thrift', contents=dedent(""" namespace py pants.constants_only const set<string> VALID_IDENTIFIERS = ["Hello", "World", "!"] """)) target = self.make_target(spec='src/thrift/exported', target_type=PythonThriftLibrary, sources=['exported.thrift'], provides=PythonArtifact(name='test.exported', version='0.0.0.')) # TODO(John Sirois): Find a way to get easy access to pants option defaults. self.set_options_for_scope( 'binaries', baseurls=['https://dl.bintray.com/pantsbuild/bin/build-support'], fetch_timeout_secs=30) with self.run_execute(target) as created: self.assertEqual([target], created)