github.com/tiagovtristao/plz@v13.4.0+incompatible/src/parse/rules/python_rules.build_defs (about) 1 """ Rules to build Python code. 2 3 The output artifacts for Python rules are .pex files (see https://github.com/pantsbuild/pex). 4 Pex is a rather nice system for combining Python code and all needed dependencies 5 (excluding the actual interpreter and possibly some system level bits) into a single file. 6 7 The process of compiling pex files can be a little slow when including many large files, as 8 often happens when one's binary includes large compiled dependencies (eg. numpy...). Hence 9 we have a fairly elaborate optimisation whereby each python_library rule builds a little 10 zipfile containing just its sources, and all of those are combined at the end to produce 11 the final .pex. This builds at roughly the same pace for a clean build of a single target, 12 but is drastically faster for building many targets with similar dependencies or rebuilding 13 a target which has only had small changes. 14 """ 15 16 17 def python_library(name:str, srcs:list=[], resources:list=[], deps:list=[], visibility:list=None, 18 test_only:bool&testonly=False, zip_safe:bool=True, labels:list&features&tags=[], interpreter:str=None, 19 strip:bool=False): 20 """Generates a Python library target, which collects Python files for use by dependent rules. 21 22 Note that each python_library performs some pre-zipping of its inputs before they're combined 23 in a python_binary or python_test. Hence while it's of course not required that all dependencies 24 of those rules are python_library rules, it's often a good idea to wrap any large dependencies 25 in one to improve incrementality (not necessary for pip_library, of course). 26 27 Args: 28 name (str): Name of the rule. 29 srcs (list): Python source files for this rule. 30 resources (list): Non-Python files that this rule collects which will be included in the final .pex. 31 The distinction between this and srcs is fairly arbitrary and historical, but 32 semantically quite nice and parallels python_test. 33 deps (list): Dependencies of this rule. 34 visibility (list): Visibility specification. 35 test_only (bool): If True, can only be depended on by tests. 36 zip_safe (bool): Should be set to False if this library can't be safely run inside a .pex 37 (the most obvious reason not is when it contains .so modules). 38 See python_binary for more information. 39 labels (list): Labels to apply to this rule. 40 interpreter (str): The Python interpreter to use. Defaults to the config setting 41 which is normally just 'python', but could be 'python3' or 42 'pypy' or whatever. 43 strip (bool): If True, the original sources are stripped and only bytecode is output. 44 """ 45 if not zip_safe: 46 labels.append('py:zip-unsafe') 47 if srcs or resources: 48 cmd = '$TOOLS_JARCAT z -d -o ${OUTS} -i .' 49 interpreter = interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER 50 if srcs: 51 # This is a bit of a hack, but rather annoying. We want to put bytecode in its 'legacy' location 52 # in python3 because zipimport doesn't look in __pycache__. Unfortunately the flag doesn't exist 53 # in python2 so we have to guess whether we should apply it or not. 54 bytecode_flag = '-b' if 'python3' in interpreter or 'pypy3' in interpreter else '' 55 compile_cmd = f'$TOOLS_INT -S -m compileall {bytecode_flag} -f $SRCS_SRCS' 56 if strip: 57 cmd = ' && '.join([compile_cmd, 'rm -f $SRCS_SRCS', cmd]) 58 else: 59 cmd = ' && '.join([compile_cmd, cmd]) 60 # Pre-zip the files for later collection by python_binary. 61 zip_rule = build_rule( 62 name=name, 63 tag='zip', 64 srcs={ 65 'SRCS': srcs, 66 'RES': resources, 67 }, 68 outs=[f'.{name}.pex.zip'], 69 cmd=cmd, 70 building_description='Compressing...', 71 requires=['py'], 72 test_only=test_only, 73 output_is_complete=True, 74 tools={ 75 'int': [interpreter], 76 'jarcat': [CONFIG.JARCAT_TOOL], 77 }, 78 ) 79 deps.append(zip_rule) 80 elif strip: 81 raise ParseError("Can't pass strip=True to a python_library with no srcs") 82 83 return filegroup( 84 name=name, 85 srcs=resources if strip else (srcs + resources), 86 deps=deps, 87 visibility=visibility, 88 output_is_complete=False, 89 requires=['py'], 90 test_only=test_only, 91 labels=labels, 92 ) 93 94 95 def python_binary(name:str, main:str, resources:list=[], out:str=None, deps:list=[], 96 visibility:list=None, zip_safe:bool=None, strip:bool=False, interpreter:str=None, 97 shebang:str='', labels:list&features&tags=[]): 98 """Generates a Python binary target. 99 100 This compiles all source files together into a single .pex file which can 101 be easily copied or deployed. The construction of the .pex is done in parts 102 by the dependent python_library rules, and this rule simply builds the 103 metadata for it and concatenates them all together. 104 105 Args: 106 name (str): Name of the rule. 107 main (str): Python file which is the entry point and __main__ module. 108 resources (list): List of static resources to include in the .pex. 109 out (str): Name of the output file. Default to name + .pex 110 deps (list): Dependencies of this rule. 111 visibility (list): Visibility specification. 112 zip_safe (bool): Allows overriding whether the output is marked zip safe or not. 113 If set to explicitly True or False, the output will be marked 114 appropriately; by default it will be safe unless any of the 115 transitive dependencies are themselves marked as not zip-safe. 116 strip (bool): Strips source code from the output .pex file, leaving just bytecode. 117 interpreter (str): The Python interpreter to use. Defaults to the config setting 118 which is normally just 'python', but could be 'python3' or 119 'pypy' or whatever. 120 shebang (str): Exact shebang to apply to the generated file. By default we will 121 determine something appropriate for the given interpreter. 122 labels (list): Labels to apply to this rule. 123 """ 124 shebang = shebang or interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER 125 cmd = '$TOOLS_PEX -s "%s" -m "%s" --zip_safe' % (shebang, CONFIG.PYTHON_MODULE_DIR) 126 pre_build, cmd = _handle_zip_safe(cmd, zip_safe) 127 128 lib_rule = python_library( 129 name='_%s#lib' % name, 130 srcs=[main], 131 resources=resources, 132 interpreter=interpreter, 133 deps=deps, 134 visibility=visibility, 135 ) 136 137 # Use the pex tool to compress the entry point & add all the bootstrap helpers etc. 138 pex_rule = build_rule( 139 name = name, 140 tag = 'pex', 141 srcs=[main], 142 outs=[f'.{name}_main.pex.zip'], # just call it .zip so everything has the same extension 143 cmd=cmd, 144 requires=['py', 'pex'], 145 pre_build=pre_build, 146 deps=deps, 147 needs_transitive_deps=True, # Needed so we can find anything with zip_safe=False on it. 148 output_is_complete=True, 149 tools={ 150 'interpreter': [interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER], 151 'pex': [CONFIG.PEX_TOOL], 152 }, 153 ) 154 # This rule concatenates the .pex with all the other precompiled zip files from dependent rules. 155 cmd = '$TOOL z -i . -s .pex.zip -s .whl --preamble_from="$SRC" --include_other --add_init_py --strict' 156 if strip: 157 cmd += ' --strip_py' 158 build_rule( 159 name=name, 160 srcs=[pex_rule], 161 deps=[lib_rule], 162 outs=[out or (name + '.pex')], 163 cmd=cmd, 164 needs_transitive_deps=True, 165 binary=True, 166 output_is_complete=True, 167 building_description="Creating pex...", 168 visibility=visibility, 169 requires=['py', interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER], 170 tools=[CONFIG.JARCAT_TOOL], 171 # This makes the python_library rule the dependency for other python_library or 172 # python_test rules that try to import it. Does mean that they cannot collect a .pex 173 # by depending directly on the rule, they'll just get the Python files instead. 174 # This is not a common case anyway; more usually you'd treat that as a runtime data 175 # file rather than trying to pack into a pex. Can be worked around with an 176 # intermediary filegroup rule if really needed. 177 provides={'py': lib_rule}, 178 labels=labels, 179 ) 180 181 182 def python_test(name:str, srcs:list, data:list=[], resources:list=[], deps:list=[], worker:str='', 183 labels:list&features&tags=None, size:str=None, flags:str='', visibility:list=None, 184 container:bool|dict=False, sandbox:bool=None, timeout:int=0, flaky:bool|int=0, 185 test_outputs:list=None, zip_safe:bool=None, interpreter:str=None): 186 """Generates a Python test target. 187 188 This works very similarly to python_binary; it is also a single .pex file 189 which is run to execute the tests. The tests are run via either unittest or pytest, depending 190 on which is set for the test runner, which can be configured either via the python_test_runner 191 package property or python.testrunner in the config. 192 193 Args: 194 name (str): Name of the rule. 195 srcs (list): Source files for this test. 196 data (list): Runtime data files for the test. 197 resources (list): Non-Python files to be included in the pex. Note that the distinction 198 vs. srcs is important here; srcs are passed to unittest for it to run 199 and it may or may not be happy if given non-Python files. 200 deps (list): Dependencies of this rule. 201 worker (str): Reference to worker script, A persistent worker process that is used to set up the test. 202 labels (list): Labels for this rule. 203 size (str): Test size (enormous, large, medium or small). 204 flags (str): Flags to apply to the test command. 205 visibility (list): Visibility specification. 206 container (bool | dict): If True, the test will be run in a container (eg. Docker). 207 sandbox (bool): Sandbox the test on Linux to restrict access to namespaces such as network. 208 timeout (int): Maximum time this test is allowed to run for, in seconds. 209 flaky (int | bool): True to mark this test as flaky, or an integer for a number of reruns. 210 test_outputs (list): Extra test output files to generate from this test. 211 zip_safe (bool): Allows overriding whether the output is marked zip safe or not. 212 If set to explicitly True or False, the output will be marked 213 appropriately; by default it will be safe unless any of the 214 transitive dependencies are themselves marked as not zip-safe. 215 interpreter (str): The Python interpreter to use. Defaults to the config setting 216 which is normally just 'python', but could be 'python3' or 217 'pypy' or whatever. 218 """ 219 timeout, labels = _test_size_and_timeout(size, timeout, labels) 220 interpreter = interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER 221 cmd = '$TOOLS_PEX -t -s "%s" -m "%s" -r "%s" --zip_safe' % (interpreter, CONFIG.PYTHON_MODULE_DIR, CONFIG.PYTHON_TEST_RUNNER) 222 pre_build, cmd = _handle_zip_safe(cmd, zip_safe) 223 224 # Use the pex tool to compress the entry point & add all the bootstrap helpers etc. 225 pex_rule = build_rule( 226 name = name, 227 tag = 'pex', 228 srcs=srcs, 229 outs=[f'.{name}_main.pex.zip'], # just call it .zip so everything has the same extension 230 cmd=cmd, 231 requires=['py'], 232 test_only=True, 233 needs_transitive_deps=True, # needed for zip-safe detection 234 building_description="Creating pex info...", 235 pre_build=pre_build, 236 deps=deps, 237 tools={ 238 'interpreter': [interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER], 239 'pex': [CONFIG.PEX_TOOL], 240 }, 241 ) 242 243 # If there are resources specified, they have to get built into the pex. 244 deps = [pex_rule] 245 lib_rule = python_library( 246 name='_%s#lib' % name, 247 srcs=srcs, 248 resources=resources, 249 interpreter=interpreter, 250 deps=deps, 251 test_only=True, 252 visibility=visibility, 253 ) 254 255 deps = [pex_rule, lib_rule] 256 257 test_cmd = f'$TEST {flags}' 258 if worker: 259 test_cmd = f'$(worker {worker}) && {test_cmd} ' 260 deps += [worker] 261 262 # This rule concatenates the .pex with all the other precompiled zip files from dependent rules. 263 build_rule( 264 name=name, 265 srcs=[pex_rule], 266 deps=deps, 267 # N.B. the actual test sources are passed as data files as well. This is needed for pytest but 268 # is faster for unittest as well (because we don't need to rebuild the pex if they change). 269 data=data + srcs, 270 outs=[f'{name}.pex'], 271 labels=labels, 272 cmd='$TOOL z -i . -s .pex.zip -s .whl --preamble_from="$SRC" --include_other --add_init_py --strict', 273 test_cmd=test_cmd, 274 needs_transitive_deps=True, 275 output_is_complete=True, 276 binary=True, 277 test=True, 278 container=container, 279 test_sandbox=sandbox, 280 building_description="Building pex...", 281 visibility=visibility, 282 test_timeout=timeout, 283 flaky=flaky, 284 test_outputs=test_outputs, 285 requires=['py', interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER], 286 tools=[CONFIG.JARCAT_TOOL], 287 ) 288 289 290 def pip_library(name:str, version:str, hashes:list=None, package_name:str=None, outs:list=None, 291 test_only:bool&testonly=False, deps:list=[], post_install_commands:list=None, 292 install_subdirectory:bool=False, repo:str=None, use_pypi:bool=None, patch:str|list=None, 293 visibility:list=None, zip_safe:bool=True, licences:list=None, pip_flags:str=None, 294 strip:list=[]): 295 """Provides a build rule for third-party dependencies to be installed by pip. 296 297 Args: 298 name (str): Name of the build rule. 299 version (str): Specific version of the package to install. 300 hashes (list): List of acceptable hashes for this target. 301 package_name (str): Name of the pip package to install. Defaults to the same as 'name'. 302 outs (list): List of output files / directories. Defaults to [name]. 303 test_only (bool): If True, can only be used by test rules or other test_only libraries. 304 deps (list): List of rules this library depends on. 305 post_install_commands (list): Commands run after pip install has completed. 306 install_subdirectory (bool): Forces the package to install into a subdirectory with this name. 307 repo (str): Allows specifying a custom repo to fetch from. 308 use_pypi (bool): If True, will check PyPI as well for packages. 309 patch (str | list): A patch file or files to be applied after install. 310 visibility (list): Visibility declaration for this rule. 311 zip_safe (bool): Flag to indicate whether a pex including this rule will be zip-safe. 312 licences (list): Licences this rule is subject to. Default attempts to detect from package metadata. 313 pip_flags (str): List of additional flags to pass to pip. 314 strip (list): Files to strip after install. Note that these are done at any level. 315 """ 316 package_name = package_name or name 317 package_name = f'{package_name}=={version}' 318 outs = outs or [name] 319 post_install_commands = post_install_commands or [] 320 post_build = None 321 use_pypi = CONFIG.USE_PYPI if use_pypi is None else use_pypi 322 index_flag = '' if use_pypi else '--no-index' 323 pip_flags = pip_flags or CONFIG.PIP_FLAGS 324 325 repo_flag = '' 326 repo = repo or CONFIG.PYTHON_DEFAULT_PIP_REPO 327 if repo: 328 if repo.startswith('//') or repo.startswith(':'): # Looks like a build label, not a URL. 329 repo_flag = f'-f %(location {repo})' 330 deps.append(repo) 331 else: 332 repo_flag = '-f ' + repo 333 334 target = outs[0] if install_subdirectory else '.' 335 336 cmd = '$TOOLS_PIP install --no-deps --no-compile --no-cache-dir --default-timeout=60 --target=' + target 337 if CONFIG.OS == 'linux': 338 # Fix for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=830892 339 # tl;dr: Debian has broken --target with a custom patch, the only way to fix is to pass --system 340 # which is itself Debian-specific, so we need to find if we're running on Debian. AAAAARGGGHHHH... 341 cmd = f'[ -f /etc/debian_version ] && [ $TOOLS_PIP == "/usr/bin/pip3" ] && SYS_FLAG="--system" || SYS_FLAG=""; {cmd} $SYS_FLAG' 342 elif CONFIG.OS == 'darwin': 343 # Fix for Homebrew which fails with a superficially similar issue. 344 # https://github.com/Homebrew/brew/blob/master/docs/Homebrew-and-Python.md suggests fixing with --install-option 345 # but that prevents downloading binary wheels. This is more fiddly but seems to work. 346 # Unfortunately it does *not* work similarly on the Debian problem :( 347 cmd = 'echo "[install]\nprefix=" > .pydistutils.cfg; ' + cmd 348 cmd += f' -b build {repo_flag} {index_flag} {pip_flags} {package_name}' 349 350 if strip: 351 cmd += ' && find . %s | xargs rm -rf' % ' -or '.join(['-name "%s"' % s for s in strip]) 352 353 if not licences: 354 cmd += ' && find . -name METADATA -or -name PKG-INFO | grep -v "^./build/" | xargs grep -E "License ?:" | grep -v UNKNOWN | cat' 355 356 if install_subdirectory: 357 cmd += f' && touch {target}/__init__.py && rm -rf {target}/*.egg-info {target}/*.dist-info' 358 359 if patch: 360 patches = [patch] if isinstance(patch, str) else patch 361 if CONFIG.OS == 'freebsd': 362 # --no-backup-if-mismatch is not supported, but we need to get rid of the .orig 363 # files for hashes to match correctly. 364 cmd += ' && ' + ' && '.join([f'patch -p0 < $(location {patch})' for patch in patches]) 365 cmd += ' && find . -name "*.orig" | xargs rm' 366 else: 367 cmd += ' && ' + ' && '.join([f'patch -p0 --no-backup-if-mismatch < $(location {patch})' for patch in patches]) 368 369 if post_install_commands: 370 cmd = ' && '.join([cmd] + post_install_commands) 371 372 # TODO(peterebden): --prefix preserves the old behaviour here, but I'm not sure how intuitive that is; leaving it 373 # out puts everything at the top level of the pex, which is more normal for Python really. 374 cmd += ' && $TOOLS_JARCAT z -d --prefix $PKG_DIR -i ' + ' -i '.join(outs) 375 376 wheel_rule = build_rule( 377 name = name, 378 tag = 'wheel', 379 cmd = cmd, 380 outs = [name + '.whl'], 381 srcs = patches if patch else [], 382 deps = deps, 383 building_description = 'Fetching...', 384 hashes = hashes, 385 requires = ['py'], 386 test_only = test_only, 387 licences = licences, 388 tools = { 389 'pip': [CONFIG.PIP_TOOL], 390 'jarcat': [CONFIG.JARCAT_TOOL], 391 }, 392 post_build = None if licences else _add_licences, 393 sandbox = False, 394 labels = ['py:zip-unsafe'] if not zip_safe else None, 395 ) 396 return build_rule( 397 name = name, 398 srcs = [wheel_rule], 399 outs = outs, 400 cmd = '$TOOL x $SRCS -s $PKG_DIR', 401 tools = [CONFIG.JARCAT_TOOL], 402 labels = ['py', 'pip:' + package_name], 403 provides = {'py': wheel_rule}, 404 visibility = visibility, 405 test_only = test_only, 406 deps = deps, 407 ) 408 409 410 def python_wheel(name:str, version:str, hashes:list=None, package_name:str=None, outs:list=None, 411 post_install_commands:list=None, patch:str|list=None, licences:list=None, 412 test_only:bool&testonly=False, repo:str=None, zip_safe:bool=True, visibility:list=None, 413 deps:list=[], name_scheme:str=None, strip:list=['*.pyc', 'tests']): 414 """Downloads a Python wheel and extracts it. 415 416 This is a lightweight pip-free alternative to pip_library which supports cross-compiling. 417 Rather than leaning on pip which is difficult to achieve reproducible builds with and 418 support on different platforms, this rule is a simple wrapper around curl and unzip. 419 Unless otherwise specified, the wheels are expected to adhere to common naming schemes, 420 such as: 421 <package_name>-<version>[-<os>-<arch>].whl 422 <package_name>-<version>[-<os>_<arch>].whl 423 <package_name>-<version>.whl 424 425 Args: 426 name (str): Name of the rule. Also doubles as the name of the package if package_name 427 is not set. 428 version (str): Version of the package to install. 429 hashes (list): List of hashes to verify the package against. 430 package_name (str): If given, overrides `name` for the name of the package to look for. 431 outs (list): List of output files. Defaults to a directory named the same as `name`. 432 post_install_commands (list): Commands to run after 'install'. 433 patch (str | list): Patch file to apply after install to fix any upstream code that has 434 done bad things. 435 licences (list): Licences that this rule is subject to. 436 test_only (bool): If True, this library can only be used by tests. 437 repo (str): Repository to download wheels from. 438 zip_safe (bool): Flag to indicate whether a pex including this rule will be zip-safe. 439 visibility (list): Visibility declaration. 440 deps (list): Dependencies of this rule. 441 name_scheme (str): The templatized wheel naming scheme (available template variables 442 are `url_base`, `package_name`, and `version`). 443 strip (list): Files to strip after install. Note that these are done at any level. 444 """ 445 package_name = package_name or name.replace('-', '_') 446 url_base = repo or CONFIG.PYTHON_WHEEL_REPO 447 if not url_base: 448 raise ParseError('python.wheel_repo is not set in the config, must pass repo explicitly ' 449 'to python_wheel') 450 urls = [] 451 if name_scheme: 452 urls.append(name_scheme.format(url_base=url_base, 453 package_name=package_name, 454 version=version)) 455 elif CONFIG.PYTHON_WHEEL_NAME_SCHEME: 456 urls.append(CONFIG.PYTHON_WHEEL_NAME_SCHEME.format(url_base=url_base, 457 package_name=package_name, 458 version=version)) 459 else: 460 # Populate urls using a reasonable set of possible wheel naming schemes. 461 # Look for an arch-specific wheel first; in some cases there can be both (e.g. protobuf 462 # has optional arch-specific bits) and we prefer the one with the cool stuff. 463 urls.append('{url_base}/{package_name}-{version}-${{OS}}-${{ARCH}}.whl'.format(url_base=url_base, 464 package_name=package_name, 465 version=version)) 466 urls.append('{url_base}/{package_name}-{version}-${{OS}}_${{ARCH}}.whl'.format(url_base=url_base, 467 package_name=package_name, 468 version=version)) 469 urls.append('{url_base}/{package_name}-{version}.whl'.format(url_base=url_base, 470 package_name=package_name, 471 version=version)) 472 473 file_rule = remote_file( 474 name = name, 475 _tag = 'download', 476 out = name + '.whl', 477 url = urls, 478 licences = licences if licences else None, 479 ) 480 481 cmd = ['$TOOL x $SRCS_SRC'] 482 483 if strip: 484 cmd += ['find . %s | xargs rm -rf' % ' -or '.join(['-name "%s"' % s for s in strip])] 485 if not licences: 486 cmd.append('find . -name METADATA -or -name PKG-INFO | grep -v "^./build/" | ' 487 'xargs grep -E "License ?:" | grep -v UNKNOWN | cat') 488 if patch: 489 patches = [patch] if isinstance(patch, str) else patch 490 cmd += [f'patch -p0 --no-backup-if-mismatch < $(location {p})' for p in patches] 491 if post_install_commands: 492 cmd += post_install_commands 493 494 cmd += ['$TOOL z -d --prefix $PKG -i ' + ' -i '.join(outs or [name])] 495 label = f'whl:{package_name}=={version}' 496 497 wheel_rule = build_rule( 498 name = name, 499 tag = 'wheel', 500 cmd = ' && '.join(cmd), 501 outs = [name + '.pex.zip'], 502 srcs = { 503 "SRC": [file_rule], 504 'RES': patches if patch else [], 505 }, 506 building_description = 'Repackaging...', 507 requires = ['py'], 508 deps = deps, 509 test_only = test_only, 510 licences = licences, 511 tools = [CONFIG.JARCAT_TOOL], 512 post_build = None if licences else _add_licences, 513 sandbox = False, 514 labels = ['py:zip-unsafe', label] if not zip_safe else [label], 515 ) 516 cmd = '$TOOL x $SRCS -s $PKG_DIR' 517 if outs: 518 # Hacky solution to handle things being in subdirectories in awkward ways. 519 before, _, after = outs[0].partition('/') 520 if after: 521 cmd = f'rm -rf {before} && {cmd}' 522 return build_rule( 523 name = name, 524 srcs = [wheel_rule], 525 hashes = hashes, # TODO(peterebden): Move this onto wheel_rule when we're willing to break hash compatibility. 526 outs = outs or [name], 527 tools = [CONFIG.JARCAT_TOOL], 528 cmd = cmd, 529 deps = deps, 530 visibility = visibility, 531 test_only = test_only, 532 labels = [label], 533 provides = {'py': wheel_rule}, 534 ) 535 536 537 def _handle_zip_safe(cmd, zip_safe): 538 """Handles the zip safe flag. Returns a tuple of (pre-build function, new command).""" 539 if zip_safe is None: 540 return lambda name: (set_command(name, cmd.replace('--zip_safe', ' --nozip_safe')) 541 if has_label(name, 'py:zip-unsafe') else None), cmd 542 elif zip_safe: 543 return None, cmd 544 else: 545 return None, cmd.replace('--zip_safe', ' --nozip_safe') 546 547 548 def _add_licences(name, output): 549 """Annotates a pip_library rule with detected licences after download.""" 550 for line in output: 551 if line.startswith('License: '): 552 for licence in line[9:].split(' or '): # Some are defined this way (eg. "PSF or ZPL") 553 add_licence(name, licence) 554 return 555 elif line.startswith('Classifier: License'): 556 # Oddly quite a few packages seem to have UNKNOWN for the licence but this Classifier 557 # section still seems to know what they are licenced as. 558 add_licence(name, line.split(' :: ')[-1]) 559 return 560 log.warning('No licence found for %s, should add licences = [...] to the rule', 561 name.lstrip('_').split('#')[0]) 562 563 564 if CONFIG.BAZEL_COMPATIBILITY: 565 py_library = python_library 566 py_binary = python_binary 567 py_test = python_test