high-egg-2153
01/18/2021, 3:38 PMhundreds-father-404
01/18/2021, 3:53 PMhigh-egg-2153
01/18/2021, 5:49 PM*dash_distribution*(
name="dist",
libraries = [
"lib_dash",
"lib_test_support"
]
)
In my goal rule I have:
@goal_rule
async def *dash_package*(console: Console, targets: Targets) -> DashPackage:
libraries = []
for target in targets:
if target.has_field(LibrariesField):
libraries = target[LibrariesField].value
So I can get the list of "libraries" no problem. Now what I would like to do is trigger the equivalent of:
./pants package lib_dash
./pants package lib_test_support
After that I need to move a bunch of files around and copy the wheels from the above two packages into the output folder.hundreds-father-404
01/18/2021, 5:53 PM./pants package path/to:dash_distribution, and that will create a Dash executable which possibly includes artifacts built from running ./pants package on those? Very similar to the archive type we have: https://www.pantsbuild.org/docs/resources#archive-create-a-zip-or-tar-filehigh-egg-2153
01/18/2021, 6:00 PMhigh-egg-2153
01/18/2021, 6:01 PMhundreds-father-404
01/18/2021, 6:05 PMOnce it's ready to be officially deployed, I need to copy sources, wheels, requirements.txt into the app's dash repository and commit the changes.This is the part that you are writing a plugin for, right? What does the final "product"/artifact look like? Is it a zipped folder for example?
hundreds-father-404
01/18/2021, 6:06 PM./pants package path/to:dash_distribution, only that that will need to create several wheels based on python_distribution targets first. Is that right?
I don't think you necessarily want a separate goal dash-package. You can do that if you want, but a goal of Pants is to give a consistent interface for users so that they don't need to remember whether it's ./pants pex-package vs. ./pants setup-py vs. ./pants dash-package, etchigh-egg-2153
01/18/2021, 6:09 PMhigh-egg-2153
01/18/2021, 6:12 PMhigh-egg-2153
01/18/2021, 6:12 PMhundreds-father-404
01/18/2021, 6:12 PMpackage goal, you'll want to follow the guide on https://www.pantsbuild.org/docs/plugins-package-goal
Ignore the part about "sources" for your new target type, that was a bad idea and we haven't finished fixing the guidance. Almost certainly, dash_distribution should not have any sources field.hundreds-father-404
01/18/2021, 6:13 PMThen either as part of or after generating these folders there would be a git push to deploy them to dash.Sounds good. To scope it for now, I would recommend not adding the git push logic yet. Only automate the step of creating that directory. That can be a followup
high-egg-2153
01/18/2021, 6:14 PMhundreds-father-404
01/18/2021, 6:34 PMdash-package, but it's not as elegant and means your users now need to remember when to use package vs. dash-packagehundreds-father-404
01/18/2021, 6:34 PM./pants package to create the wheels? It'll help to understand how to give advice on how to run that in your new rulehigh-egg-2153
01/18/2021, 6:46 PMdef *package*(target):
dist_folder = 'dist/' + target
if not os.path.exists(dist_folder):
os.makedirs(dist_folder)
prepare_dist_folder(dist_folder)
# Copy root folder files.
for f in glob.glob(target + '/*.py') + ['requirements.txt']:
if not f.endswith('_test.py') and not f.endswith('conftest.py'):
shutil.copy(f, dist_folder)
# Copy assets.
assets_path = target + "/assets"
if os.path.exists(assets_path):
dist_assets_path = dist_folder + "/assets/"
if not os.path.exists(dist_assets_path):
os.makedirs(dist_assets_path)
shutil.copy(assets_path, dist_assets_path)
with *open*(target + '/BUILD_DEPS', 'r') as fd:
dependencies = fd.read().split(',')
with *open*(dist_folder + '/requirements.txt', 'a') as requirements:
for dependency in dependencies:
# Package dependency.
os.system('./pants package :' + dependency)
# copy dependencies to dist folder
wheel_name = dependency + "-0.0.0-py3-none-any.whl"
dist_packages_folder = dist_folder + '/packages/'
os.makedirs(dist_packages_folder, exist_ok=True)
shutil.copy('dist/' + wheel_name, dist_packages_folder)
# Append wheels to requirements.txt
requirements.write('./packages/' + wheel_name)hundreds-father-404
01/18/2021, 6:50 PM./pants package on python_distribution targets?
That's the part I'm confused on: are you making wheels for your first-party distributions or also for third-party dependencies? If the latter, did you create a dedicated python_distribution target for each requirement in your requirements.txt?high-egg-2153
01/18/2021, 6:54 PMhigh-egg-2153
01/18/2021, 6:55 PMhundreds-father-404
01/18/2021, 6:58 PM./pants package in your new rule.
https://github.com/pantsbuild/pants/blob/c55fd827252fb1288c1f1d1c7e0af78b551336e1/src/python/pants/core/target_types.py#L285-L291
Where package_targets is Iterable[Target], meaning it can be the targets that you were collecting into a list with your target.has_field(LibrariesField) snippet
Basically, this snippet is going to convert the Target objects into things called `FieldSet`s that are ready to work with the rule to create wheels. In the next line, you then invoke that rulehundreds-father-404
01/18/2021, 6:59 PMBuiltPackage objects, defined in package.py. Each of those has a digest: Digest property, which will give you access to the final result through the file system API https://www.pantsbuild.org/docs/rules-api-file-systemhigh-egg-2153
01/18/2021, 7:02 PMhigh-egg-2153
01/18/2021, 8:04 PMclass *DashDependencies*(*SpecialCasedDependencies*):
alias = "dash_dependencies"
*help* = ("")
class *DashDistributionTarget*(*Target*):
"""Dash App Deployment."""
alias = "dash_distribution"
core_fields = (
*COMMON_TARGET_FIELDS,
OutputPathField,
DashDependencies,
)
*@dataclass*(frozen=True)
class *DashFieldSet*(*PackageFieldSet*):
required_fields = ()
dependencies: DashDependencies
output_path: OutputPathField
*@rule*(level=LogLevel.DEBUG)
async def *dash_package*(field_set: DashFieldSet) -> BuiltPackage:
dependency_targets = await Get(Targets, UnparsedAddressInputs, field_<http://set.dependencies.to|set.dependencies.to>_unparsed_address_inputs())
package_field_sets_per_target = await Get(
FieldSetsPerTarget, FieldSetsPerTargetRequest(PackageFieldSet, dependency_targets)
)
packages = await MultiGet(
Get(BuiltPackage, PackageFieldSet, field_set)
for field_set in package_field_sets_per_target.field_sets
)
for p in packages:
*print*(p)high-egg-2153
01/18/2021, 8:04 PM