Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Sign In

pybuilder / pybuilder / 2399701812

28 May 2022 - 3:00 coverage decreased (-0.002%) to 81.336%
2399701812

Pull #855

github

GitHub
Merge a34fde08e into 8731de99a
Pull Request #855: Make sure that project.description is always set

1741 of 2261 branches covered (77.0%)

Branch coverage included in aggregate %.

4 of 5 new or added lines in 1 file covered. (80.0%)

5079 of 6124 relevant lines covered (82.94%)

46.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.98
/src/main/python/pybuilder/plugins/python/distutils_plugin.py
1
#   -*- coding: utf-8 -*-
2
#
3
#   This file is part of PyBuilder
4
#
5
#   Copyright 2011-2020 PyBuilder Team
6
#
7
#   Licensed under the Apache License, Version 2.0 (the "License");
8
#   you may not use this file except in compliance with the License.
9
#   You may obtain a copy of the License at
10
#
11
#       http://www.apache.org/licenses/LICENSE-2.0
12
#
13
#   Unless required by applicable law or agreed to in writing, software
14
#   distributed under the License is distributed on an "AS IS" BASIS,
15
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
#   See the License for the specific language governing permissions and
17
#   limitations under the License.
18

19
import io
59×
20
import os
59×
21
import re
59×
22
import string
59×
23
from datetime import datetime
59×
24
from textwrap import dedent
59×
25

26
from pybuilder import pip_utils
59×
27
from pybuilder.core import (after,
59×
28
                            before,
29
                            use_plugin,
30
                            init,
31
                            task,
32
                            RequirementsFile,
33
                            Dependency)
34
from pybuilder.errors import BuildFailedException, MissingPrerequisiteException
59×
35
from pybuilder.python_utils import StringIO
59×
36
from pybuilder.utils import (as_list,
59×
37
                             is_string,
38
                             is_notstr_iterable,
39
                             get_dist_version_string,
40
                             safe_log_file_name,
41
                             tail_log)
42

43
use_plugin("python.core")
59×
44

45
LEADING_TAB_RE = re.compile(r'^(\t*)')
59×
46
DATA_FILES_PROPERTY = "distutils_data_files"
59×
47
SETUP_TEMPLATE = string.Template("""#!/usr/bin/env python
59×
48
#   -*- coding: utf-8 -*-
49
$remove_hardlink_capabilities_for_shared_filesystems
50
from $module import setup
51
from $module.command.install import install as _install
52

53
class install(_install):
54
    def pre_install_script(self):
55
$preinstall_script
56

57
    def post_install_script(self):
58
$postinstall_script
59

60
    def run(self):
61
        self.pre_install_script()
62

63
        _install.run(self)
64

65
        self.post_install_script()
66

67
if __name__ == '__main__':
68
    setup(
69
        name = $name,
70
        version = $version,
71
        description = $summary,
72
        long_description = $description,
73
        long_description_content_type = $description_content_type,
74
        classifiers = $classifiers,
75
        keywords = $setup_keywords,
76

77
        author = $author,
78
        author_email = $author_email,
79
        maintainer = $maintainer,
80
        maintainer_email = $maintainer_email,
81

82
        license = $license,
83

84
        url = $url,
85
        project_urls = $project_urls,
86

87
        scripts = $scripts,
88
        packages = $packages,
89
        namespace_packages = $namespace_packages,
90
        py_modules = $modules,
91
        entry_points = $entry_points,
92
        data_files = $data_files,
93
        package_data = $package_data,
94
        install_requires = $dependencies,
95
        dependency_links = $dependency_links,
96
        zip_safe = $zip_safe,
97
        cmdclass = {'install': install},
98
        python_requires = $python_requires,
99
        obsoletes = $obsoletes,
100
    )
101
""")
102

103

104
def default(value, default=""):
59×
105
    if value is None:
59×
106
        return default
59×
107
    return value
59×
108

109

110
def as_str(value):
59×
111
    return repr(str(value))
59×
112

113

114
@init
59×
115
def initialize_distutils_plugin(project):
116
    project.plugin_depends_on("pypandoc", "~=1.4")
59×
117
    project.plugin_depends_on("setuptools", ">=38.6.0")
59×
118
    project.plugin_depends_on("twine", ">=1.15.0")
59×
119
    project.plugin_depends_on("wheel", ">=0.34.0")
59×
120

121
    project.set_property_if_unset("distutils_commands", ["sdist", "bdist_wheel"])
59×
122
    project.set_property_if_unset("distutils_command_options", None)
59×
123

124
    # Workaround for http://bugs.python.org/issue8876 , unable to build a bdist
125
    # on a filesystem that does not support hardlinks
126
    project.set_property_if_unset("distutils_issue8876_workaround_enabled", False)
59×
127
    project.set_property_if_unset("distutils_classifiers", [
59×
128
        "Development Status :: 3 - Alpha",
129
        "Programming Language :: Python"
130
    ])
131
    project.set_property_if_unset("distutils_use_setuptools", True)
59×
132

133
    project.set_property_if_unset("distutils_fail_on_warnings", False)
59×
134

135
    project.set_property_if_unset("distutils_upload_register", False)
59×
136
    project.set_property_if_unset("distutils_upload_repository", None)
59×
137
    project.set_property_if_unset("distutils_upload_repository_key", None)
59×
138
    project.set_property_if_unset("distutils_upload_sign", False)
59×
139
    project.set_property_if_unset("distutils_upload_sign_identity", None)
59×
140
    project.set_property_if_unset("distutils_upload_skip_existing", False)
59×
141

142
    project.set_property_if_unset("distutils_readme_description", False)
59×
143
    project.set_property_if_unset("distutils_readme_file", "README.md")
59×
144
    project.set_property_if_unset("distutils_readme_file_convert", False)
59×
145
    project.set_property_if_unset("distutils_readme_file_type", None)
59×
146
    project.set_property_if_unset("distutils_readme_file_encoding", None)
59×
147
    project.set_property_if_unset("distutils_readme_file_variant", None)
59×
148
    project.set_property_if_unset("distutils_summary_overwrite", False)
59×
149
    project.set_property_if_unset("distutils_description_overwrite", False)
59×
150

151
    project.set_property_if_unset("distutils_console_scripts", None)
59×
152
    project.set_property_if_unset("distutils_entry_points", None)
59×
153
    project.set_property_if_unset("distutils_setup_keywords", None)
59×
154
    project.set_property_if_unset("distutils_zip_safe", True)
59×
155

156

157
@after("prepare")
59×
158
def set_description(project, logger, reactor):
159
    if project.get_property("distutils_readme_description"):
59×
160
        description = None
16×
161
        if project.get_property("distutils_readme_file_convert"):
Branches [[0, 162]] missed. 16×
162
            try:
!
163
                reactor.pybuilder_venv.verify_can_execute(["pandoc", "--version"], "pandoc", "distutils")
!
164
                description = doc_convert(project, logger)
!
165
            except (MissingPrerequisiteException, ImportError):
!
166
                logger.warn("Was unable to find pandoc or pypandoc and did not convert the documentation")
!
167
        else:
168
            with io.open(project.expand_path("$distutils_readme_file"), "rt",
16×
169
                         encoding=project.get_property("distutils_readme_file_encoding")) as f:
170
                description = f.read()
16×
171

172
        if description:
Branches [[0, 183]] missed. 16×
173
            if (not hasattr(project, "summary") or
Branches [[0, 176]] missed. 16×
174
                    project.summary is None or
175
                    project.get_property("distutils_summary_overwrite")):
176
                setattr(project, "summary", description.splitlines()[0].strip())
!
177

178
            if (not hasattr(project, "description") or
Branches [[0, 183]] missed. 16×
179
                    project.description is None or
180
                    project.get_property("distutils_description_overwrite")):
181
                setattr(project, "description", description)
16×
182

183
    if (not hasattr(project, "description") or
59×
184
            not project.description):
185
        if hasattr(project, "summary") and project.summary:
Branches [[0, 186]] missed. 59×
NEW
186
            description = project.summary
!
187
        else:
188
            description = project.name
59×
189

190
        setattr(project, "description", description)
59×
191

192
    warn = False
59×
193
    if len(project.summary) >= 512:
Branches [[0, 194]] missed. 59×
194
        logger.warn("Project summary SHOULD be shorter than 512 characters per PEP-426")
!
195
        warn = True
!
196

197
    if "\n" in project.summary or "\r" in project.summary:
Branches [[0, 198]] missed. 59×
198
        logger.warn("Project summary SHOULD NOT contain new-line characters per PEP-426")
!
199
        warn = True
!
200

201
    if len(project.summary) >= 2048:
Branches [[0, 202]] missed. 59×
202
        raise BuildFailedException("Project summary MUST NOT be shorter than 2048 characters per PEP-426")
!
203

204
    if warn and project.get_property("distutils_fail_on_warnings"):
Branches [[0, 205]] missed. 59×
205
        raise BuildFailedException("Distutil plugin warnings caused a build failure. Please see warnings above.")
!
206

207

208
@after("package")
59×
209
def write_setup_script(project, logger):
210
    setup_script = project.expand_path("$dir_dist", "setup.py")
59×
211
    logger.info("Writing setup.py as %s", setup_script)
59×
212

213
    with io.open(setup_script, "wt", encoding="utf-8") as setup_file:
59×
214
        script = render_setup_script(project)
59×
215
        setup_file.write(script)
59×
216

217
    os.chmod(setup_script, 0o755)
59×
218

219

220
def render_setup_script(project):
59×
221
    author = ", ".join(map(lambda a: a.name, project.authors))
59×
222
    author_email = ", ".join(map(lambda a: a.email, project.authors))
59×
223
    maintainer = ", ".join(map(lambda a: a.name, project.maintainers))
59×
224
    maintainer_email = ",".join(map(lambda a: a.email, project.maintainers))
59×
225

226
    template_values = {
59×
227
        "module": "setuptools" if project.get_property("distutils_use_setuptools") else "distutils.core",
228
        "name": as_str(project.name),
229
        "version": as_str(project.dist_version),
230
        "summary": as_str(default(project.summary)),
231
        "description": as_str(default(project.description)),
232
        "description_content_type": repr(_get_description_content_type(project)),
233
        "author": as_str(author),
234
        "author_email": as_str(author_email),
235
        "maintainer": as_str(maintainer),
236
        "maintainer_email": as_str(maintainer_email),
237
        "license": as_str(default(project.license)),
238
        "url": as_str(default(project.url)),
239
        "project_urls": build_map_string(project.urls),
240
        "scripts": build_scripts_string(project),
241
        "packages": build_packages_string(project),
242
        "namespace_packages": build_namespace_packages_string(project),
243
        "modules": build_modules_string(project),
244
        "classifiers": build_classifiers_string(project),
245
        "entry_points": build_entry_points_string(project),
246
        "data_files": build_data_files_string(project),
247
        "package_data": build_package_data_string(project),
248
        "dependencies": build_install_dependencies_string(project),
249
        "dependency_links": build_dependency_links_string(project),
250
        "remove_hardlink_capabilities_for_shared_filesystems": (
251
            "import os\ndel os.link"
252
            if project.get_property("distutils_issue8876_workaround_enabled")
253
            else ""),
254
        "preinstall_script": _normalize_setup_post_pre_script(project.setup_preinstall_script or "pass"),
255
        "postinstall_script": _normalize_setup_post_pre_script(project.setup_postinstall_script or "pass"),
256
        "setup_keywords": build_setup_keywords(project),
257
        "python_requires": as_str(default(project.requires_python)),
258
        "obsoletes": build_string_from_array(project.obsoletes),
259
        "zip_safe": project.get_property("distutils_zip_safe")
260
    }
261

262
    return SETUP_TEMPLATE.substitute(template_values)
59×
263

264

265
@after("package")
59×
266
def write_manifest_file(project, logger):
267
    if len(project.manifest_included_files) == 0 and len(project.manifest_included_directories) == 0:
59×
268
        logger.debug("No data to write into MANIFEST.in")
59×
269
        return
59×
270

271
    logger.debug("Files included in MANIFEST.in: %s" %
59×
272
                 project.manifest_included_files)
273

274
    manifest_filename = project.expand_path("$dir_dist", "MANIFEST.in")
59×
275
    logger.info("Writing MANIFEST.in as %s", manifest_filename)
59×
276

277
    with open(manifest_filename, "w") as manifest_file:
59×
278
        manifest_file.write(render_manifest_file(project))
59×
279

280
    os.chmod(manifest_filename, 0o664)
59×
281

282

283
@before("publish")
59×
284
def build_binary_distribution(project, logger, reactor):
285
    logger.info("Building binary distribution in %s",
59×
286
                project.expand_path("$dir_dist"))
287

288
    commands = [build_command_with_options(cmd, project.get_property("distutils_command_options"))
59×
289
                for cmd in as_list(project.get_property("distutils_commands"))]
290
    execute_distutils(project, logger, reactor.pybuilder_venv, commands, True)
59×
291
    upload_check(project, logger, reactor)
59×
292

293

294
@task("install")
59×
295
def install_distribution(project, logger, reactor):
296
    logger.info("Installing project %s-%s", project.name, project.version)
59×
297

298
    _prepare_reports_dir(project)
59×
299
    outfile_name = project.expand_path("$dir_reports", "distutils",
59×
300
                                       "pip_install_%s" % datetime.utcnow().strftime("%Y%m%d%H%M%S"))
301
    pip_utils.pip_install(
59×
302
        install_targets=project.expand_path("$dir_dist"),
303
        python_env=reactor.python_env_registry["system"],
304
        index_url=project.get_property("install_dependencies_index_url"),
305
        extra_index_url=project.get_property("install_dependencies_extra_index_url"),
306
        force_reinstall=True,
307
        logger=logger,
308
        verbose=project.get_property("pip_verbose"),
309
        cwd=".",
310
        outfile_name=outfile_name,
311
        error_file_name=outfile_name)
312

313

314
@task("upload", description="Upload a project to PyPi.")
59×
315
def upload(project, logger, reactor):
316
    repository = project.get_property("distutils_upload_repository")
59×
317
    repository_args = []
59×
318
    if repository:
59×
319
        repository_args = ["--repository-url", repository]
59×
320
    else:
321
        repository_key = project.get_property("distutils_upload_repository_key")
59×
322
        if repository_key:
59×
323
            repository_args = ["--repository", repository_key]
59×
324

325
    upload_sign = project.get_property("distutils_upload_sign")
59×
326
    sign_identity = project.get_property("distutils_upload_sign_identity")
59×
327
    upload_sign_args = []
59×
328
    if upload_sign:
59×
329
        upload_sign_args = ["--sign"]
59×
330
        if sign_identity:
59×
331
            upload_sign_args += ["--identity", sign_identity]
59×
332

333
    if project.get_property("distutils_upload_register"):
59×
334
        logger.info("Registering project %s-%s%s", project.name, project.version,
59×
335
                    (" into repository '%s'" % repository) if repository else "")
336
        execute_twine(project, logger, reactor.pybuilder_venv, repository_args, "register")
59×
337

338
    skip_existing = project.get_property("distutils_upload_skip_existing")
59×
339
    logger.info("Uploading project %s-%s%s%s%s%s", project.name, project.version,
59×
340
                (" to repository '%s'" % repository) if repository else "",
341
                get_dist_version_string(project, " as version %s"),
342
                (" signing%s" % (" with %s" % sign_identity if sign_identity else "")) if upload_sign else "",
343
                (", will skip existing" if skip_existing else ""))
344

345
    upload_cmd_args = repository_args + upload_sign_args
59×
346
    if skip_existing:
Branches [[0, 347]] missed. 59×
347
        upload_cmd_args.append("--skip-existing")
!
348

349
    execute_twine(project, logger, reactor.pybuilder_venv, upload_cmd_args, "upload")
59×
350

351

352
def upload_check(project, logger, reactor):
59×
353
    logger.info("Running Twine check for generated artifacts")
59×
354
    execute_twine(project, logger, reactor.pybuilder_venv, [], "check")
59×
355

356

357
def render_manifest_file(project):
59×
358
    manifest_content = StringIO()
59×
359

360
    for included_file in project.manifest_included_files:
59×
361
        manifest_content.write("include %s\n" % included_file)
59×
362

363
    for directory, pattern_list in project.manifest_included_directories:
59×
364
        patterns = ' '.join(pattern_list)
59×
365
        manifest_content.write("recursive-include %s %s\n" % (directory, patterns))
59×
366

367
    return manifest_content.getvalue()
59×
368

369

370
def build_command_with_options(command, distutils_command_options=None):
59×
371
    commands = [command]
59×
372
    if distutils_command_options:
59×
373
        try:
59×
374
            command_options = as_list(distutils_command_options[command])
59×
375
            commands.extend(command_options)
59×
376
        except KeyError:
59×
377
            pass
59×
378
    return commands
59×
379

380

381
def execute_distutils(project, logger, python_env, distutils_commands, clean=False):
59×
382
    reports_dir = _prepare_reports_dir(project)
59×
383
    setup_script = project.expand_path("$dir_dist", "setup.py")
59×
384

385
    for command in distutils_commands:
59×
386
        if is_string(command):
59×
387
            out_file = os.path.join(reports_dir, safe_log_file_name(command))
59×
388
        else:
389
            out_file = os.path.join(reports_dir, safe_log_file_name("__".join(command)))
59×
390
        with open(out_file, "w") as out_f:
59×
391
            commands = python_env.executable + [setup_script]
59×
392
            if project.get_property("verbose"):
59×
393
                commands.append("-v")
16×
394
            if clean:
59×
395
                commands.extend(["clean", "--all"])
59×
396
            if is_string(command):
59×
397
                commands.extend(command.split())
59×
398
            else:
399
                commands.extend(command)
59×
400
            logger.debug("Executing distutils command: %s", commands)
59×
401
            return_code = python_env.run_process_and_wait(commands, project.expand_path("$dir_dist"), out_f)
59×
402
            if return_code != 0:
Branches [[0, 403]] missed. 59×
403
                raise BuildFailedException(
!
404
                    "Error while executing setup command %s. See %s for full details:\n%s",
405
                    command, out_file, tail_log(out_file))
406

407

408
def execute_twine(project, logger, python_env, command_args, command):
59×
409
    reports_dir = _prepare_reports_dir(project)
59×
410
    dist_artifact_dir, artifacts = _get_generated_artifacts(project, logger)
59×
411

412
    if command == "register":
59×
413
        for artifact in artifacts:
59×
414
            out_file = os.path.join(reports_dir,
59×
415
                                    safe_log_file_name("twine_%s_%s.log" % (command, os.path.basename(artifact))))
416
            _execute_twine(project, logger, python_env,
59×
417
                           [command] + command_args + [artifact], dist_artifact_dir, out_file)
418
    else:
419
        out_file = os.path.join(reports_dir, safe_log_file_name("twine_%s.log" % command))
59×
420
        _execute_twine(project, logger, python_env,
59×
421
                       [command] + command_args + artifacts, dist_artifact_dir, out_file)
422

423

424
def _execute_twine(project, logger, python_env, command, work_dir, out_file):
59×
425
    with open(out_file, "w") as out_f:
59×
426
        commands = python_env.executable + ["-m", "twine"] + command
59×
427
        logger.debug("Executing Twine: %s", commands)
59×
428
        return_code = python_env.run_process_and_wait(commands, work_dir, out_f)
59×
429
        if return_code != 0:
Branches [[0, 430]] missed. 59×
430
            raise BuildFailedException(
!
431
                "Error while executing Twine %s. See %s for full details:\n%s", command, out_file, tail_log(out_file))
432

433

434
def strip_comments(requirements):
59×
435
    return [requirement for requirement in requirements
59×
436
            if not requirement.strip().startswith("#")]
437

438

439
def quote(requirements):
59×
440
    return ['"%s"' % requirement for requirement in requirements]
59×
441

442

443
def is_editable_requirement(requirement):
59×
444
    return "-e " in requirement or "--editable " in requirement
59×
445

446

447
def flatten_and_quote(requirements_file):
59×
448
    with open(requirements_file.name, 'r') as requirements_file:
59×
449
        requirements = [requirement.strip("\n") for requirement in requirements_file.readlines()]
59×
450
        requirements = [requirement for requirement in requirements if requirement]
59×
451
        return quote(strip_comments(requirements))
59×
452

453

454
def format_single_dependency(dependency):
59×
455
    return '%s%s' % (dependency.name, pip_utils.build_dependency_version_string(dependency))
59×
456

457

458
def build_install_dependencies_string(project):
59×
459
    dependencies = [
59×
460
        dependency for dependency in project.dependencies
461
        if isinstance(dependency, Dependency) and not dependency.url]
462
    requirements = [
59×
463
        requirement for requirement in project.dependencies
464
        if isinstance(requirement, RequirementsFile)]
465
    if not dependencies and not requirements:
59×
466
        return "[]"
59×
467

468
    dependencies = [format_single_dependency(dependency) for dependency in dependencies]
59×
469
    requirements = [strip_comments(flatten_and_quote(requirement)) for requirement in requirements]
59×
470
    flattened_requirements = [dependency for dependency_list in requirements for dependency in dependency_list]
59×
471
    flattened_requirements_without_editables = [
59×
472
        requirement for requirement in flattened_requirements if not is_editable_requirement(requirement)]
473

474
    dependencies.extend(flattened_requirements_without_editables)
59×
475

476
    for i, dep in enumerate(dependencies):
59×
477
        if dep.startswith('"') and dep.endswith('"'):
59×
478
            dependencies[i] = dep[1:-1]
59×
479

480
    return build_string_from_array(dependencies)
59×
481

482

483
def build_dependency_links_string(project):
59×
484
    dependency_links = [
59×
485
        dependency for dependency in project.dependencies
486
        if isinstance(dependency, Dependency) and dependency.url]
487
    requirements = [
59×
488
        requirement for requirement in project.dependencies
489
        if isinstance(requirement, RequirementsFile)]
490

491
    editable_links_from_requirements = []
59×
492
    for requirement in requirements:
59×
493
        editables = [editable for editable in flatten_and_quote(requirement) if is_editable_requirement(editable)]
59×
494
        editable_links_from_requirements.extend(
59×
495
            [editable.replace("--editable ", "").replace("-e ", "") for editable in editables])
496

497
    if not dependency_links and not requirements:
59×
498
        return "[]"
59×
499

500
    def format_single_dependency(dependency):
59×
501
        return '%s' % dependency.url
59×
502

503
    all_dependency_links = [link for link in map(format_single_dependency, dependency_links)]
59×
504
    all_dependency_links.extend(editable_links_from_requirements)
59×
505

506
    for i, dep in enumerate(all_dependency_links):
59×
507
        if dep.startswith('"') and dep.endswith('"'):
59×
508
            all_dependency_links[i] = dep[1:-1]
59×
509

510
    return build_string_from_array(all_dependency_links)
59×
511

512

513
def build_scripts_string(project):
59×
514
    scripts = [script for script in project.list_scripts()]
59×
515

516
    scripts_dir = project.get_property("dir_dist_scripts")
59×
517
    if scripts_dir:
59×
518
        scripts = list(map(lambda s: '{}/{}'.format(scripts_dir, s), scripts))
59×
519

520
    return build_string_from_array(scripts)
59×
521

522

523
def build_data_files_string(project):
59×
524
    indent = 8
59×
525
    """
526
    data_files = [
527
      ('bin', ['foo','bar','hhrm'])
528
    ]
529
    """
530
    data_files = project.files_to_install
59×
531
    if not len(data_files):
59×
532
        return '[]'
59×
533

534
    result = "[\n"
59×
535

536
    for dataType, dataFiles in data_files:
59×
537
        result += (" " * (indent + 4)) + "('%s', ['" % dataType
59×
538
        result += "', '".join(dataFiles)
59×
539
        result += "']),\n"
59×
540

541
    result = result[:-2] + "\n"
59×
542
    result += " " * indent + "]"
59×
543
    return result
59×
544

545

546
def build_package_data_string(project):
59×
547
    package_data = project.package_data
59×
548
    if not package_data:
59×
549
        return "{}"
59×
550

551
    indent = 8
59×
552

553
    sorted_keys = sorted(project.package_data.keys())
59×
554

555
    result = "{\n"
59×
556

557
    for pkgType in sorted_keys:
59×
558
        result += " " * (indent + 4)
59×
559
        result += "'%s': " % pkgType
59×
560
        result += "['"
59×
561
        result += "', '".join(package_data[pkgType])
59×
562
        result += "'],\n"
59×
563

564
    result = result[:-2] + "\n"
59×
565
    result += " " * indent + "}"
59×
566

567
    return result
59×
568

569

570
def build_map_string(m):
59×
571
    if not m:
59×
572
        return "{}"
59×
573

574
    indent = 8
59×
575

576
    sorted_keys = sorted(m.keys())
59×
577

578
    result = "{\n"
59×
579

580
    for k in sorted_keys:
59×
581
        result += " " * (indent + 4)
59×
582
        result += "%r: %r,\n" % (k, m[k])
59×
583

584
    result = result[:-2] + "\n"
59×
585
    result += " " * indent + "}"
59×
586

587
    return result
59×
588

589

590
def build_namespace_packages_string(project):
59×
591
    return build_string_from_array([pkg for pkg in project.explicit_namespaces])
59×
592

593

594
def build_packages_string(project):
59×
595
    return build_string_from_array([pkg for pkg in project.list_packages()])
59×
596

597

598
def build_modules_string(project):
59×
599
    return build_string_from_array([mod for mod in project.list_modules()])
59×
600

601

602
def build_entry_points_string(project):
59×
603
    console_scripts = project.get_property('distutils_console_scripts')
59×
604
    entry_points = project.get_property('distutils_entry_points')
59×
605
    if console_scripts is not None and entry_points is not None:
59×
606
        raise BuildFailedException("'distutils_console_scripts' cannot be combined with 'distutils_entry_points'")
59×
607

608
    if entry_points is None:
59×
609
        entry_points = dict()
59×
610

611
    if console_scripts is not None:
59×
612
        entry_points['console_scripts'] = console_scripts
59×
613

614
    if len(entry_points) == 0:
59×
615
        return '{}'
59×
616

617
    indent = 8
59×
618
    result = "{\n"
59×
619

620
    for k in sorted(entry_points.keys()):
59×
621
        result += " " * (indent + 4)
59×
622
        result += "'%s': %s" % (k, build_string_from_array(as_list(entry_points[k]), indent + 8)) + ",\n"
59×
623

624
    result = result[:-2] + "\n"
59×
625
    result += (" " * indent) + "}"
59×
626

627
    return result
59×
628

629

630
def build_setup_keywords(project):
59×
631
    setup_keywords = project.get_property("distutils_setup_keywords")
59×
632
    if not setup_keywords or not len(setup_keywords):
59×
633
        return repr("")
59×
634

635
    if isinstance(setup_keywords, (list, tuple)):
59×
636
        return repr(" ".join(setup_keywords))
59×
637

638
    return repr(setup_keywords)
59×
639

640

641
def build_classifiers_string(project):
59×
642
    classifiers = project.get_property("distutils_classifiers", [])
59×
643
    return build_string_from_array(classifiers, indent=12)
59×
644

645

646
def build_string_from_array(arr, indent=12):
59×
647
    result = ""
59×
648

649
    if len(arr) == 1:
59×
650
        """
651
        arrays with one item contained on one line
652
        """
653
        if len(arr[0]) > 0:
59×
654
            if is_notstr_iterable(arr[0]):
59×
655
                result += "[" + build_string_from_array(arr[0], indent + 4) + "]"
59×
656
            else:
657
                result += "['%s']" % arr[0]
59×
658
        else:
659
            result = '[[]]'
59×
660
    elif len(arr) > 1:
59×
661
        result = "[\n"
59×
662

663
        for item in arr:
59×
664
            if is_notstr_iterable(item):
59×
665
                result += (" " * indent) + build_string_from_array(item, indent + 4) + ",\n"
59×
666
            else:
667
                result += (" " * indent) + "'" + item + "',\n"
59×
668
        result = result[:-2] + "\n"
59×
669
        result += " " * (indent - 4)
59×
670
        result += "]"
59×
671
    else:
672
        result = '[]'
59×
673

674
    return result
59×
675

676

677
def build_string_from_dict(d, indent=12):
59×
678
    element_separator = ",\n"
!
679
    element_separator += " " * indent
!
680
    map_elements = []
!
681

682
    for k, v in d.items():
Branches [[0, 683], [0, 685]] missed. !
683
        map_elements.append("'%s': '%s'" % (k, v))
!
684

685
    result = ""
!
686

687
    if len(map_elements) > 0:
Branches [[0, 688], [0, 695]] missed. !
688
        result += "{\n"
!
689
        result += " " * indent
!
690
        result += element_separator.join(map_elements)
!
691
        result += "\n"
!
692
        result += " " * (indent - 4)
!
693
        result += "}"
!
694

695
    return result
!
696

697

698
def doc_convert(project, logger):
59×
699
    import pypandoc
!
700
    readme_file = project.expand_path("$distutils_readme_file")
!
701
    logger.debug("Converting %s into RST format for PyPi documentation...", readme_file)
!
702
    return pypandoc.convert_file(readme_file, "rst")
!
703

704

705
def _expand_leading_tabs(s, indent=4):
59×
706
    def replace_tabs(match):
59×
707
        return " " * (len(match.groups(0)) * indent)
59×
708

709
    return "".join([LEADING_TAB_RE.sub(replace_tabs, line) for line in s.splitlines(True)])
59×
710

711

712
def _normalize_setup_post_pre_script(s, indent=8):
59×
713
    indent_str = " " * indent
59×
714
    return "".join([indent_str + line if len(str.rstrip(line)) > 0 else line for line in
59×
715
                    dedent(_expand_leading_tabs(s)).splitlines(True)])
716

717

718
def _prepare_reports_dir(project):
59×
719
    reports_dir = project.expand_path("$dir_reports", "distutils")
59×
720
    if not os.path.exists(reports_dir):
59×
721
        os.mkdir(reports_dir)
59×
722
    return reports_dir
59×
723

724

725
def _get_description_content_type(project):
59×
726
    file_type = project.get_property("distutils_readme_file_type")
59×
727
    file_encoding = project.get_property("distutils_readme_file_encoding")
59×
728
    file_variant = project.get_property("distutils_readme_file_variant")
59×
729

730
    if not file_type:
Branches [[0, 740]] missed. 59×
731
        if project.get_property("distutils_readme_description"):
59×
732
            readme_file_ci = project.get_property("distutils_readme_file").lower()
16×
733
            if readme_file_ci.endswith("md"):
Branches [[0, 735]] missed. 16×
734
                file_type = "text/markdown"
16×
735
            elif readme_file_ci.endswith("rst"):
Branches [[0, 736], [0, 738]] missed. !
736
                file_type = "text/x-rst"
!
737
            else:
738
                file_type = "text/plain"
!
739

740
    if file_encoding:
Branches [[0, 741]] missed. 59×
741
        file_encoding = file_encoding.upper()
!
742

743
    if file_type == "text/markdown":
59×
744
        if file_variant:
Branches [[0, 745]] missed. 16×
745
            file_variant = file_variant.upper()
!
746

747
    if file_type:
59×
748
        return "%s%s%s" % (file_type,
16×
749
                           "; charset=%s" % file_encoding if file_encoding else "",
750
                           "; variant=%s" % file_variant if file_variant else "")
751

752

753
def _get_generated_artifacts(project, logger):
59×
754
    dist_artifact_dir = project.expand_path("$dir_dist", "dist")
59×
755

756
    artifacts = [os.path.join(dist_artifact_dir, artifact) for artifact in list(os.walk(dist_artifact_dir))[0][2]]
59×
757
    return dist_artifact_dir, artifacts
59×
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
BLOG · TWITTER · Legal & Privacy · Supported CI Services · What's a CI service? · Automated Testing

© 2022 Coveralls, Inc