Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sage
Path: blob/develop/build/sage_bootstrap/expand_class.py
4052 views
1
# -*- coding: utf-8 -*-
2
"""
3
Utility to manage lists of packages
4
"""
5
6
# ****************************************************************************
7
# Copyright (C) 2016 Volker Braun <[email protected]>
8
# 2020-2024 Matthias Koeppe
9
#
10
# This program is free software: you can redistribute it and/or modify
11
# it under the terms of the GNU General Public License as published by
12
# the Free Software Foundation, either version 2 of the License, or
13
# (at your option) any later version.
14
# https://www.gnu.org/licenses/
15
# ****************************************************************************
16
17
import logging
18
from sage_bootstrap.package import Package
19
20
log = logging.getLogger()
21
22
23
class PackageClass(object):
24
25
def __init__(self, *package_names_or_classes, **filters):
26
self.__names = set()
27
exclude = filters.pop('exclude', ())
28
include_dependencies = filters.pop('include_dependencies', False)
29
exclude_dependencies = filters.pop('exclude_dependencies', False)
30
filenames = filters.pop('has_files', [])
31
no_filenames = filters.pop('no_files', [])
32
excluded = []
33
for package_names in exclude:
34
excluded.extend(package_names)
35
if filters:
36
raise ValueError('filter not supported')
37
38
def included_in_filter(pkg):
39
if not all(any(pkg.has_file(filename)
40
for filename in filename_disjunction.split('|'))
41
for filename_disjunction in filenames):
42
return False
43
return not any(any(pkg.has_file(filename)
44
for filename in no_filename_disjunction.split('|'))
45
for no_filename_disjunction in no_filenames)
46
47
for package_name_or_class in package_names_or_classes:
48
if package_name_or_class == ':all:':
49
self._init_all(predicate=included_in_filter)
50
elif package_name_or_class == ':standard:':
51
self._init_standard(predicate=included_in_filter)
52
elif package_name_or_class == ':optional:':
53
self._init_optional(predicate=included_in_filter)
54
elif package_name_or_class == ':experimental:':
55
self._init_experimental(predicate=included_in_filter)
56
elif any(package_name_or_class.startswith(prefix)
57
for prefix in ["pkg:", "pypi/", "generic"]):
58
self.__names.add(Package(package_name_or_class).name)
59
else:
60
if ':' in package_name_or_class:
61
raise ValueError('a colon may only appear in a PURL such as '
62
'pkg:pypi/DISTRIBUTION-NAME '
63
'and in designators of package types, '
64
'which must be one of '
65
':all:, :standard:, :optional:, or :experimental:'
66
'got {}'.format(package_name_or_class))
67
if '-' in package_name_or_class:
68
raise ValueError('dashes may only appear in a PURL such as '
69
'pkg:pypi/DISTRIBUTION-NAME; '
70
'SPKG names use underscores')
71
self.__names.add(package_name_or_class)
72
73
def include_recursive_dependencies(names, package_name):
74
if package_name in names:
75
return
76
try:
77
pkg = Package(package_name)
78
except FileNotFoundError:
79
# Silently ignore unknown packages,
80
# substitutions such as $(BLAS) $(PYTHON),
81
# and optional dependencies of the form $(find-string ...).
82
return
83
names.add(package_name)
84
for dependency in pkg.dependencies:
85
include_recursive_dependencies(names, dependency)
86
87
if include_dependencies:
88
package_names = set()
89
for name in self.__names:
90
include_recursive_dependencies(package_names, name)
91
self.__names = package_names
92
93
def exclude_recursive_dependencies(names, package_name):
94
try:
95
pkg = Package(package_name)
96
except FileNotFoundError:
97
return
98
for dependency in pkg.dependencies:
99
names.discard(dependency)
100
exclude_recursive_dependencies(names, dependency)
101
102
if exclude_dependencies:
103
for name in list(self.__names):
104
exclude_recursive_dependencies(self.__names, name)
105
106
self.__names.difference_update(excluded)
107
108
@property
109
def names(self):
110
return sorted(self.__names)
111
112
def _init_all(self, predicate):
113
self.__names.update(pkg.name for pkg in Package.all() if predicate(pkg))
114
115
def _init_standard(self, predicate):
116
self.__names.update(pkg.name for pkg in Package.all() if pkg.type == 'standard' and predicate(pkg))
117
118
def _init_optional(self, predicate):
119
self.__names.update(pkg.name for pkg in Package.all() if pkg.type == 'optional' and predicate(pkg))
120
121
def _init_experimental(self, predicate):
122
self.__names.update(pkg.name for pkg in Package.all() if pkg.type == 'experimental' and predicate(pkg))
123
124
def apply(self, function, *args, **kwds):
125
for package_name in self.names:
126
function(package_name, *args, **kwds)
127
128