amuse-fi
Advanced tools
| Metadata-Version: 2.1 | ||
| Name: amuse-fi | ||
| Version: 2023.4.0 | ||
| Version: 2023.5.0 | ||
| Summary: The Astrophysical Multipurpose Software Environment - FI | ||
@@ -5,0 +5,0 @@ Home-page: http://www.amusecode.org/ |
+1
-1
| Metadata-Version: 2.1 | ||
| Name: amuse-fi | ||
| Version: 2023.4.0 | ||
| Version: 2023.5.0 | ||
| Summary: The Astrophysical Multipurpose Software Environment - FI | ||
@@ -5,0 +5,0 @@ Home-page: http://www.amusecode.org/ |
+1
-1
| [build-system] | ||
| requires = [ "setuptools", "wheel", "amuse-framework" ] | ||
| requires = ["setuptools>=65.0.0", "wheel>=0.30", "amuse-framework>=2023.5.0"] |
| # coding: utf-8 | ||
| # file generated by setuptools_scm | ||
| # don't change, don't track in version control | ||
| version = '2023.4.0' | ||
| version_tuple = (2023, 4, 0) | ||
| version = '2023.5.0' | ||
| version_tuple = (2023, 5, 0) |
+347
-351
@@ -1,30 +0,24 @@ | ||
| __revision__ = "$Id:$" | ||
| import warnings | ||
| import sys, os, re, subprocess | ||
| import sys | ||
| import os | ||
| import os.path | ||
| import re | ||
| import datetime | ||
| import stat | ||
| from copy import deepcopy | ||
| from os.path import abspath | ||
| from . import supportrc | ||
| try: | ||
| import numpy | ||
| except ImportError: | ||
| warnings.warn( "numpy etc needed during build; operation may fail" ) | ||
| warnings.warn("numpy needed during build; operation may fail") | ||
| import configparser | ||
| from io import StringIO | ||
| from stat import ST_MODE | ||
| from distutils import sysconfig | ||
| from distutils.core import Command | ||
| from distutils.dep_util import newer | ||
| from subprocess import Popen, PIPE, STDOUT | ||
| from glob import glob | ||
| from distutils.dir_util import create_tree | ||
| from distutils.util import convert_path | ||
| from distutils import log | ||
| # from distutils import log | ||
| import logging | ||
| from distutils import spawn | ||
@@ -34,3 +28,4 @@ from distutils import file_util | ||
| from distutils.command.clean import clean | ||
| from distutils.command.install import install | ||
| from setuptools.command.install import install | ||
| from setuptools import Command | ||
| from setuptools.command.build import build | ||
@@ -40,7 +35,7 @@ from setuptools.command.develop import develop | ||
| from subprocess import call, Popen, PIPE, STDOUT | ||
| from . import supportrc | ||
| if supportrc["framework_install"]: | ||
| from .generate_main import generate_main | ||
| try: | ||
@@ -53,4 +48,2 @@ from numpy.distutils import fcompiler | ||
| first_line_re = re.compile('^#!.*python[0-9.]*([ \t].*)?$') | ||
| from glob import glob | ||
@@ -78,7 +71,7 @@ def pyfiles_in_build_dir(builddir): | ||
| negative_opt = {'no-inplace':'inplace'} | ||
| negative_opt = {'no-inplace': 'inplace'} | ||
| boolean_options = ['inplace'] | ||
| def initialize_options (self): | ||
| def initialize_options(self): | ||
| self.codes_dir = None | ||
@@ -90,10 +83,10 @@ self.lib_dir = None | ||
| self.install_data = None | ||
| self.root=None | ||
| def finalize_options (self): | ||
| self.root = None | ||
| def finalize_options(self): | ||
| self.set_undefined_options( | ||
| 'install', | ||
| ('build_lib', 'build_lib'), | ||
| ('root', 'root'), | ||
| ('install_data', 'install_data'), | ||
| ('build_lib', 'build_lib'), | ||
| ('root', 'root'), | ||
| ('install_data', 'install_data'), | ||
| ) | ||
@@ -103,3 +96,3 @@ | ||
| 'build', | ||
| ('build_temp', 'build_temp'), | ||
| ('build_temp', 'build_temp'), | ||
| ) | ||
@@ -116,6 +109,6 @@ | ||
| else: | ||
| self.lib_dir=os.path.join(self.build_temp, 'lib') | ||
| self.lib_dir = os.path.join(self.build_temp, 'lib') | ||
| def run(self): | ||
| data_dir = os.path.join(self.install_data,'share','amuse') # for the moment add to amuse.. | ||
| data_dir = os.path.join(self.install_data, 'share', 'amuse') # for the moment add to amuse.. | ||
| data_dir = os.path.abspath(data_dir) | ||
@@ -125,15 +118,15 @@ | ||
| # '*.h', '*.a', '*.mod', '*.inc', '*.so', '*.dylib' | ||
| files=[os.path.join(dp, f) for dp, dn, fn in os.walk(self.lib_dir) for f in fn] | ||
| ext=['.h', '.a', '.mod', '.inc', '.so', '.dylib'] | ||
| files=[f for f in files if (os.path.splitext(f)[1] in ext)] | ||
| files=[os.path.relpath(f,self.lib_dir) for f in files] | ||
| create_tree(os.path.join(data_dir,'lib'), files) | ||
| files = [os.path.join(dp, f) for dp, dn, fn in os.walk(self.lib_dir) for f in fn] | ||
| ext = ['.h', '.a', '.mod', '.inc', '.so', '.dylib'] | ||
| files = [f for f in files if (os.path.splitext(f)[1] in ext)] | ||
| files = [os.path.relpath(f, self.lib_dir) for f in files] | ||
| create_tree(os.path.join(data_dir, 'lib'), files) | ||
| for f in files: | ||
| src=os.path.join(self.lib_dir,f) | ||
| target=os.path.join(data_dir,'lib', f) | ||
| self.copy_file(src,target) | ||
| src = os.path.join(self.lib_dir, f) | ||
| target = os.path.join(data_dir, 'lib', f) | ||
| self.copy_file(src, target) | ||
| class GenerateInstallIni(Command): | ||
| user_options = ( | ||
| user_options = ( | ||
| ('build-dir=', 'd', "directory to install to"), | ||
@@ -144,5 +137,5 @@ ('install-data=', None, "installation directory for data files"), | ||
| ) | ||
| boolean_options = ['force'] | ||
| def initialize_options(self): | ||
@@ -153,3 +146,3 @@ self.build_dir = None | ||
| self.root = None | ||
| def finalize_options(self): | ||
@@ -163,14 +156,13 @@ self.set_undefined_options( | ||
| ) | ||
| def run(self): | ||
| outfilename = os.path.join(self.build_dir, supportrc["package_name"], 'amuserc') | ||
| # this does not work for pip installs | ||
| #~ data_dir = os.path.join(self.install_data,'share','amuse') | ||
| #~ if not self.root is None: | ||
| #~ data_dir = os.path.relpath(data_dir,self.root) | ||
| #~ data_dir = os.path.join('/',data_dir) | ||
| #~ else: | ||
| #~ data_dir = os.path.abspath(data_dir) | ||
| # data_dir = os.path.join(self.install_data,'share','amuse') | ||
| # if not self.root is None: | ||
| # data_dir = os.path.relpath(data_dir,self.root) | ||
| # data_dir = os.path.join('/',data_dir) | ||
| # else: | ||
| # data_dir = os.path.abspath(data_dir) | ||
@@ -181,10 +173,10 @@ installinilines = [] | ||
| installinilines.append('use_python_interpreter=1') | ||
| #installinilines.append('worker_code_directory={0}'.format(os.path.join(data_dir, 'bin'))) | ||
| # installinilines.append('worker_code_directory={0}'.format(os.path.join(data_dir, 'bin'))) | ||
| if sys.platform == 'win32': | ||
| installinilines.append('worker_code_suffix=".exe"') | ||
| installinilines.append('[data]') | ||
| #~ installinilines.append('input_data_root_directory={0}'.format(os.path.join(data_dir, 'data'))) | ||
| # installinilines.append('input_data_root_directory={0}'.format(os.path.join(data_dir, 'data'))) | ||
| installinilines.append('output_data_root_directory=_amuse_output_data') | ||
| #~ installinilines.append('amuse_root_dir={0}'.format(data_dir)) | ||
| # installinilines.append('amuse_root_dir={0}'.format(data_dir)) | ||
| if 'BUILD_BINARY' in os.environ: | ||
@@ -196,4 +188,2 @@ installinilines.append('[test]') | ||
| file_util.write_file(outfilename, installinilines) | ||
@@ -226,11 +216,11 @@ | ||
| negative_opt = {'no-inplace':'inplace'} | ||
| negative_opt = {'no-inplace': 'inplace'} | ||
| boolean_options = ['force', 'inplace', 'debug', 'variant'] | ||
| def initialize_options (self): | ||
| def initialize_options(self): | ||
| self.codes_dir = None | ||
| self.lib_dir = None | ||
| self.lib_src_dir = None | ||
| self.amuse_src_dir = os.path.join('src',supportrc["package_name"]) | ||
| self.amuse_src_dir = os.path.join('src', supportrc["package_name"]) | ||
| self.environment = {} | ||
@@ -242,3 +232,3 @@ self.environment_notset = {} | ||
| self.inplace = False | ||
| self.build_lib = None | ||
@@ -249,14 +239,12 @@ self.build_temp = None | ||
| def finalize_options (self): | ||
| def finalize_options(self): | ||
| self.set_undefined_options( | ||
| 'build', | ||
| ('build_lib', 'build_lib'), | ||
| ('build_temp', 'build_temp'), | ||
| ('debug', 'debug'), | ||
| ('force', 'force'), | ||
| ('build_lib', 'build_lib'), | ||
| ('build_temp', 'build_temp'), | ||
| ('debug', 'debug'), | ||
| ('force', 'force'), | ||
| ) | ||
| self.config=None | ||
| self.config = None | ||
@@ -266,3 +254,3 @@ if supportrc["framework_install"]: | ||
| from . import config | ||
| self.config=config | ||
| self.config = config | ||
| except ImportError: | ||
@@ -273,3 +261,3 @@ # continue | ||
| from amuse import config | ||
| self.config=config | ||
| self.config = config | ||
@@ -281,5 +269,5 @@ if self.codes_dir is None: | ||
| else: | ||
| #~ self.codes_dir = os.path.join(self.build_temp, 'src', 'amuse', 'community') | ||
| # self.codes_dir = os.path.join(self.build_temp, 'src', 'amuse', 'community') | ||
| self.codes_dir = os.path.join(self.build_temp, 'codes') | ||
| self.codes_src_dir = os.path.join(self.amuse_src_dir,'community') | ||
| self.codes_src_dir = os.path.join(self.amuse_src_dir, 'community') | ||
| else: | ||
@@ -290,5 +278,5 @@ if self.inplace: | ||
| self.codes_src_dir = self.codes_dir | ||
| #~ self.codes_dir=os.path.join(self.build_temp, 'src', 'amuse', 'community') | ||
| self.codes_dir=os.path.join(self.build_temp, 'codes') | ||
| # self.codes_dir = os.path.join(self.build_temp, 'src', 'amuse', 'community') | ||
| self.codes_dir = os.path.join(self.build_temp, 'codes') | ||
| if self.lib_dir is None: | ||
@@ -312,10 +300,9 @@ if self.inplace: | ||
| self.environment['PYTHON'] = sys.executable | ||
| self.set_cuda_variables() | ||
| self.set_mpi_variables() | ||
| self.set_compiler_variables() | ||
| self.set_fortran_variables() | ||
| if 'FORTRAN' in self.environment: | ||
@@ -329,3 +316,3 @@ self.environment['F90'] = self.environment['FORTRAN'] | ||
| self.save_cfgfile_if_not_exists() | ||
| if 'MSYSCON' in os.environ: | ||
@@ -342,10 +329,10 @@ pass | ||
| if self.inplace: | ||
| self.environment['AMUSE_DIR'] = os.path.abspath(os.getcwd()) | ||
| self.environment['AMUSE_DIR'] = os.path.abspath(os.getcwd()) | ||
| else: | ||
| self.environment['AMUSE_DIR'] = os.path.abspath(self.build_temp) | ||
| self.environment['AMUSE_DIR'] = os.path.abspath(self.build_temp) | ||
| if self.inplace: | ||
| self.environment['MUSE_PACKAGE_DIR'] = os.path.abspath(os.getcwd()) | ||
| self.environment['MUSE_PACKAGE_DIR'] = os.path.abspath(os.getcwd()) | ||
| else: | ||
| self.environment['MUSE_PACKAGE_DIR'] = os.path.abspath(self.build_temp) | ||
| self.environment['MUSE_PACKAGE_DIR'] = os.path.abspath(self.build_temp) | ||
@@ -356,44 +343,43 @@ | ||
| return | ||
| if 'FORTRAN' in os.environ: | ||
| self.environment['FORTRAN'] = os.environ['FORTRAN'] | ||
| return | ||
| if self.config: | ||
| self.environment['FORTRAN'] = self.config.compilers.fc | ||
| return | ||
| if 'FC' in os.environ: | ||
| self.environment['FORTRAN'] = os.environ['FC'] | ||
| return | ||
| if 'FORT' in os.environ: | ||
| self.environment['FORTRAN'] = os.environ['FORT'] | ||
| return | ||
| if 'F90' in os.environ: | ||
| self.environment['FORTRAN'] = os.environ['F90'] | ||
| return | ||
| mpif90 = os.environ['MPIF90'] if 'MPIF90' in os.environ else 'mpif90' | ||
| try: | ||
| process = Popen([mpif90,'-show'], stdout = PIPE, stderr = PIPE) | ||
| process = Popen([mpif90, '-show'], stdout=PIPE, stderr=PIPE) | ||
| stdoutstring, stderrstring = process.communicate() | ||
| if process.returncode == 0: | ||
| parts = stdoutstring.split() | ||
| self.environment['FORTRAN'] = parts[0] | ||
| self.environment['FORTRAN'] = parts[0] | ||
| return | ||
| process = Popen([mpif90,'--showme '], stdout = PIPE, stderr = PIPE) | ||
| process = Popen([mpif90, '--showme '], stdout=PIPE, stderr=PIPE) | ||
| stdoutstring, stderrstring = process.communicate() | ||
| if process.returncode == 0: | ||
| parts = stdoutstring.split() | ||
| self.environment['FORTRAN'] = parts[0] | ||
| return | ||
| self.environment['FORTRAN'] = parts[0] | ||
| return | ||
| except: | ||
| pass | ||
| if fcompiler: | ||
| if fcompiler: | ||
| compiler = fcompiler.new_fcompiler(requiref90=True) | ||
@@ -403,5 +389,3 @@ if compiler is not None: | ||
| self.environment['FORTRAN'] = fortran_executable | ||
| def is_mpi_enabled(self): | ||
@@ -412,3 +396,3 @@ if self.config and hasattr(self.config.mpi, 'is_enabled'): | ||
| return True | ||
| def set_cuda_variables(self): | ||
@@ -425,5 +409,5 @@ all_found = True | ||
| raise DistutilsError("configuration is not up to date for cuda, please reconfigure amuse by running 'configure --enable-cuda'") | ||
| return | ||
| if self.config and not self.config.cuda.is_enabled: | ||
@@ -442,11 +426,11 @@ self.found_cuda = True | ||
| break | ||
| if all_found: | ||
| cuda_dir = self.environment['CUDA_TK'] | ||
| self.environment['CUDA_LIBDIRS'] = '-L'+cuda_dir+'/lib' + ' -L'+cuda_dir+'/lib64' | ||
| self.environment['CUDA_LIBDIRS'] = '-L'+cuda_dir+'/lib' + ' -L'+cuda_dir+'/lib64' | ||
| self.environment['CUDA_LIBS'] = '-lcudart' | ||
| return | ||
| dir = spawn.find_executable('nvcc') | ||
| if dir is None: | ||
| directory = spawn.find_executable('nvcc') | ||
| if directory is None: | ||
| self.found_cuda = False | ||
@@ -456,3 +440,3 @@ self.environment_notset['CUDA_SDK'] = '<directory>' | ||
| return | ||
| cuda_dir = os.path.dirname(os.path.dirname(dir)) | ||
| cuda_dir = os.path.dirname(os.path.dirname(directory)) | ||
| self.environment['CUDA_LIBDIRS'] = '-L'+cuda_dir+'/lib' + ' -L'+cuda_dir+'/lib64' | ||
@@ -473,7 +457,6 @@ self.environment['CUDA_LIBS'] = '-lcudart' | ||
| def set_compiler_variables(self): | ||
| if self.config and not hasattr(self.config.compilers, 'found_fftw'): | ||
| raise DistutilsError("configuration is not up to date, please reconfigure amuse by running 'configure'") | ||
| if self.config: | ||
@@ -486,12 +469,11 @@ self.environment['CXX'] = self.config.compilers.cxx | ||
| self.environment['FFLAGS'] = self.config.compilers.fc_flags | ||
| if self.config.compilers.found_fftw == 'yes': | ||
| self.environment['FFTW_FLAGS'] = self.config.compilers.fftw_flags | ||
| self.environment['FFTW_LIBS'] = self.config.compilers.fftw_libs | ||
| if self.config.compilers.found_gsl == 'yes': | ||
| self.environment['GSL_FLAGS'] = self.config.compilers.gsl_flags | ||
| self.environment['GSL_LIBS'] = self.config.compilers.gsl_libs | ||
| return | ||
@@ -517,3 +499,3 @@ | ||
| self.environment['OPENMP_CFLAGS'] = '' | ||
| def set_libdir_variables(self): | ||
@@ -523,8 +505,8 @@ for varname in ('SAPPORO_LIBDIRS', 'GRAPE6_LIBDIRS'): | ||
| continue | ||
| if varname in os.environ: | ||
| self.environment[varname] = os.environ[varname] | ||
| else: | ||
| self.environment_notset[varname] ='-L<directory>' | ||
| self.environment_notset[varname] = '-L<directory>' | ||
| if 'SAPPORO_LIBDIRS' in self.environment: | ||
@@ -550,3 +532,3 @@ self.environment['SAPPORO_LIBS'] = '-L{0} -lsapporo'.format( | ||
| self.environment['BOOSTLIBS'] = '' | ||
| def set_libs_variables(self): | ||
@@ -556,3 +538,3 @@ for varname, libname in []: | ||
| continue | ||
| if varname in os.environ: | ||
@@ -564,9 +546,9 @@ self.environment[varname] = os.environ[varname] | ||
| def copy_config_to_build_dir(self): | ||
| configpath=os.path.abspath(os.getcwd()) | ||
| configpath = os.path.abspath(os.getcwd()) | ||
| if self.inplace: | ||
| topath=self.amuse_src_dir | ||
| topath = self.amuse_src_dir | ||
| else: | ||
| topath=os.path.join(self.build_lib, "amuse") | ||
| self.copy_file(os.path.join(configpath,"config.mk"), topath) | ||
| topath = os.path.join(self.build_lib, "amuse") | ||
| self.copy_file(os.path.join(configpath, "config.mk"), topath) | ||
| def copy_build_prereq_to_build_dir(self): | ||
@@ -577,28 +559,27 @@ if not os.path.exists(self.build_temp): | ||
| if supportrc["framework_install"]: | ||
| configpath=os.path.abspath(os.getcwd()) | ||
| self.copy_file(os.path.join(configpath,"config.mk"), self.build_temp) | ||
| #~ self.copy_tree(os.path.join(configpath,"support"), os.path.join(self.build_temp,"support") ) | ||
| #~ self.copy_tree(os.path.join(configpath,"src"), os.path.join(self.build_temp,"src") ) | ||
| path=os.path.join(self.build_temp,"src") | ||
| configpath = os.path.abspath(os.getcwd()) | ||
| self.copy_file(os.path.join(configpath, "config.mk"), self.build_temp) | ||
| # self.copy_tree(os.path.join(configpath,"support"), os.path.join(self.build_temp,"support") ) | ||
| # self.copy_tree(os.path.join(configpath,"src"), os.path.join(self.build_temp,"src") ) | ||
| path = os.path.join(self.build_temp, "src") | ||
| if not os.path.exists(path) and not os.path.islink(path): | ||
| os.symlink(os.path.relpath(self.build_lib,self.build_temp), path) | ||
| os.symlink(os.path.relpath(self.build_lib, self.build_temp), path) | ||
| def copy_codes_to_build_dir(self): | ||
| for dir in self.makefile_paths(self.codes_src_dir): | ||
| reldir = os.path.relpath(dir, self.codes_src_dir) | ||
| for directory in self.makefile_paths(self.codes_src_dir): | ||
| reldir = os.path.relpath(directory, self.codes_src_dir) | ||
| self.copy_tree( | ||
| dir, | ||
| directory, | ||
| os.path.join(self.codes_dir, reldir) | ||
| ) | ||
| def copy_lib_to_build_dir(self): | ||
| for dir in self.makefile_paths(self.lib_src_dir): | ||
| reldir = os.path.relpath(dir, self.lib_src_dir) | ||
| for directory in self.makefile_paths(self.lib_src_dir): | ||
| reldir = os.path.relpath(directory, self.lib_src_dir) | ||
| self.copy_tree( | ||
| dir, | ||
| directory, | ||
| os.path.join(self.lib_dir, reldir) | ||
| ) | ||
| def copy_worker_codes_to_build_dir(self): | ||
@@ -610,23 +591,21 @@ if sys.platform == 'win32': | ||
| worker_so_re = re.compile(r'(([a-zA-Z0-9]+_)*)?cython(_[a-zA-Z0-9]+)?.so') | ||
| lib_binbuilddir = os.path.join(self.build_lib, supportrc["package_name"], '_workers') | ||
| if not os.path.exists(lib_binbuilddir): | ||
| self.mkpath(lib_binbuilddir) | ||
| for srcdir in self.makefile_paths(self.codes_src_dir): | ||
| reldir = os.path.relpath(srcdir, self.codes_src_dir) | ||
| temp_builddir = os.path.join(self.codes_dir, reldir) | ||
| self.announce("will copy worker: {0}".format(srcdir), level = log.INFO) | ||
| self.announce("will copy worker: {0}".format(srcdir), level=logging.INFO) | ||
| lib_builddir = os.path.join(self.build_lib, os.path.relpath(srcdir, os.path.join(self.amuse_src_dir, '..'))) | ||
| shortname = reldir.lower() | ||
| self.announce(shortname, level = log.INFO) | ||
| self.announce(shortname, level=logging.INFO) | ||
| for name in os.listdir(temp_builddir): | ||
| path = os.path.join(temp_builddir, name) | ||
| stat = os.stat(path) | ||
| if os.path.isfile(path): | ||
@@ -638,4 +617,3 @@ if worker_so_re.match(name): | ||
| #self.announce("will copy worker: {0}".format(name), level = log.INFO) | ||
| # self.announce("will copy worker: {0}".format(name), level = logging.INFO) | ||
| if os.path.isfile(path) and os.access(path, os.X_OK): | ||
@@ -646,11 +624,11 @@ if worker_code_re.match(name): | ||
| elif not name.endswith('.py'): | ||
| self.announce("will not copy executable: {0}, it does not match the worker pattern".format(name), level = log.WARN) | ||
| self.announce("will not copy executable: {0}, it does not match the worker pattern".format(name), level=logging.WARN) | ||
| # also copy file or dir named data | ||
| path=os.path.join(temp_builddir,'data') | ||
| path = os.path.join(temp_builddir, 'data') | ||
| topath = os.path.join(lib_builddir, 'data') | ||
| if os.path.isfile(path): | ||
| self.copy_file(path, topath) | ||
| self.copy_file(path, topath) | ||
| if os.path.isdir(path): | ||
| self.copy_tree(path, topath) | ||
| self.copy_tree(path, topath) | ||
@@ -663,12 +641,12 @@ def copy_worker_codes(self): | ||
| worker_so_re = re.compile(r'(([a-zA-Z0-9]+_)*)?cython(_[a-zA-Z0-9]+)?.so') | ||
| for srcdir in self.makefile_paths(self.codes_src_dir): | ||
| reldir = os.path.relpath(srcdir, self.codes_src_dir) | ||
| temp_builddir = os.path.join(self.codes_dir, reldir) | ||
| self.announce("will copy worker: {0}".format(srcdir), level = log.INFO) | ||
| self.announce("will copy worker: {0}".format(srcdir), level=logging.INFO) | ||
| lib_builddir = os.path.join(self.build_lib, os.path.relpath(srcdir, os.path.join(self.amuse_src_dir, '..'))) | ||
| shortname = reldir.lower() | ||
| self.announce(shortname, level = log.INFO) | ||
| self.announce(shortname, level=logging.INFO) | ||
@@ -678,3 +656,3 @@ for name in os.listdir(temp_builddir): | ||
| stat = os.stat(path) | ||
| if os.path.isfile(path): | ||
@@ -691,13 +669,13 @@ if worker_so_re.match(name): | ||
| elif not name.endswith('.py'): | ||
| self.announce("will not copy executable: {0}, it does not match the worker pattern".format(name), level = log.WARN) | ||
| self.announce("will not copy executable: {0}, it does not match the worker pattern".format(name), level=logging.WARN) | ||
| # also copy file or dir named data | ||
| path=os.path.join(temp_builddir,'data') | ||
| path = os.path.join(temp_builddir, 'data') | ||
| topath = os.path.join(lib_builddir, 'data') | ||
| if os.path.isfile(path): | ||
| self.copy_file(path, topath) | ||
| self.copy_file(path, topath) | ||
| if os.path.isdir(path): | ||
| self.copy_tree(path, topath) | ||
| def subdirs_in_path(self,path): | ||
| def subdirs_in_path(self, path): | ||
| if not os.path.exists(path): | ||
@@ -710,3 +688,3 @@ return | ||
| continue | ||
| path_ = os.path.join(path, name) | ||
@@ -716,3 +694,3 @@ if os.path.isdir(path_): | ||
| def makefile_paths(self,path): | ||
| def makefile_paths(self, path): | ||
| for x in self.subdirs_in_path(path): | ||
@@ -735,3 +713,3 @@ for name in ('makefile', 'Makefile'): | ||
| del self.environment_notset[varname] | ||
| def save_cfgfile_if_not_exists(self): | ||
@@ -743,14 +721,11 @@ if not os.path.exists('amuse.cfg'): | ||
| config.set('environment', name, value) | ||
| for name, value in self.environment_notset.items(): | ||
| config.set('environment', name, '') | ||
| with open('amuse.cfg', 'w') as f: | ||
| config.write(f) | ||
| def get_special_targets(self, name, directory, environment): | ||
| process = Popen(['make','-qp', '-C', directory], env = environment, stdout = PIPE, stderr = PIPE) | ||
| process = Popen(['make', '-qp', '-C', directory], env=environment, stdout=PIPE, stderr=PIPE) | ||
| stdoutstring, stderrstring = process.communicate() | ||
@@ -769,3 +744,4 @@ stdoutstring = str(stdoutstring, 'utf-8') | ||
| targetname = line[len('muse_worker_'):index_of_the_colon] | ||
| if '%' not in targetname: result.append((line[:index_of_the_colon], targetname,)) | ||
| if '%' not in targetname: | ||
| result.append((line[:index_of_the_colon], targetname,)) | ||
| elif line.startswith('worker_code_'): | ||
@@ -775,3 +751,4 @@ index_of_the_colon = line.index(':') | ||
| targetname = line[len('worker_code_'):index_of_the_colon] | ||
| if '%' not in targetname: result.append((line[:index_of_the_colon], targetname,)) | ||
| if '%' not in targetname: | ||
| result.append((line[:index_of_the_colon], targetname,)) | ||
| elif line.startswith(name + '_worker_'): | ||
@@ -781,20 +758,21 @@ index_of_the_colon = line.index(':') | ||
| targetname = line[len(name + '_worker_'):index_of_the_colon] | ||
| if '%' not in targetname: result.append((line[:index_of_the_colon], targetname,)) | ||
| result=list(set(result)) | ||
| if '%' not in targetname: | ||
| result.append((line[:index_of_the_colon], targetname,)) | ||
| result = list(set(result)) | ||
| return result | ||
| def call(self, arguments, buildlogfile = None, **keyword_arguments): | ||
| def call(self, arguments, buildlogfile=None, **keyword_arguments): | ||
| stringio = [] | ||
| self.announce(' '.join(arguments), log.DEBUG) | ||
| self.announce(' '.join(arguments), logging.DEBUG) | ||
| process = Popen( | ||
| arguments, | ||
| stdout = PIPE, | ||
| stderr = STDOUT, | ||
| arguments, | ||
| stdout=PIPE, | ||
| stderr=STDOUT, | ||
| **keyword_arguments | ||
| ) | ||
| while True: | ||
@@ -804,60 +782,58 @@ line = process.stdout.readline() | ||
| break | ||
| if not buildlogfile is None: | ||
| if buildlogfile is not None: | ||
| buildlogfile.write(line) | ||
| self.announce(line[:-1].decode("utf-8"), log.DEBUG) | ||
| self.announce(line[:-1].decode("utf-8"), logging.DEBUG) | ||
| stringio.append(str(line, 'utf-8')) | ||
| result = process.wait() | ||
| content = ''.join(stringio) | ||
| if result!=0: | ||
| self.announce("error in call, tail output:\n", log.INFO) | ||
| self.announce(''.join(stringio[-100:]), log.INFO) | ||
| self.announce("-"*80, log.INFO) | ||
| if result != 0: | ||
| self.announce("error in call, tail output:\n", logging.INFO) | ||
| self.announce(''.join(stringio[-100:]), logging.INFO) | ||
| self.announce("-"*80, logging.INFO) | ||
| return result, content | ||
| def build_environment(self): | ||
| environment=self.environment.copy() | ||
| environment = self.environment.copy() | ||
| environment.update(os.environ) | ||
| path=os.path.join(environment["MUSE_PACKAGE_DIR"],"src") | ||
| if environment["MUSE_PACKAGE_DIR"]!=environment["AMUSE_DIR"]: | ||
| path=path+":"+os.path.join(environment["AMUSE_DIR"],"src") | ||
| path=path+':'+environment.get("PYTHONPATH", "") | ||
| environment["PYTHONPATH"]=path | ||
| path = os.path.join(environment["MUSE_PACKAGE_DIR"], "src") | ||
| if environment["MUSE_PACKAGE_DIR"] != environment["AMUSE_DIR"]: | ||
| path = path+":"+os.path.join(environment["AMUSE_DIR"], "src") | ||
| path = path+':'+environment.get("PYTHONPATH", "") | ||
| environment["PYTHONPATH"] = path | ||
| return environment | ||
| def do_clean(self): | ||
| environment = self.build_environment() | ||
| environment = self.build_environment() | ||
| for x in self.makefile_paths(self.lib_dir): | ||
| self.announce("cleaning libary " + x) | ||
| self.call(['make','-C', x, 'clean'], env=environment) | ||
| self.call(['make', '-C', x, 'clean'], env=environment) | ||
| for x in self.makefile_paths(self.codes_dir): | ||
| if os.path.exists(x): | ||
| self.announce("cleaning " + x) | ||
| self.call(['make','-C', x, 'clean'], env=environment) | ||
| self.call(['make', '-C', x, 'clean'], env=environment) | ||
| def do_distclean(self): | ||
| environment = self.build_environment() | ||
| environment = self.build_environment() | ||
| for x in self.makefile_paths(self.lib_dir): | ||
| self.announce("cleaning libary:" + x) | ||
| self.call(['make','-C', x, 'distclean'], env=environment) | ||
| self.call(['make', '-C', x, 'distclean'], env=environment) | ||
| for x in self.makefile_paths(self.codes_dir): | ||
| self.announce("cleaning community code:" + x) | ||
| self.call(['make','-C', x, 'distclean'], env=environment) | ||
| self.call(['make', '-C', x, 'distclean'], env=environment) | ||
| class SplitOutput(object) : | ||
| def __init__(self, file1, file2) : | ||
| class SplitOutput(object): | ||
| def __init__(self, file1, file2): | ||
| self.file1 = file1 | ||
| self.file2 = file2 | ||
| def __del__(self) : | ||
| def __del__(self): | ||
| self.close() | ||
@@ -869,11 +845,11 @@ | ||
| def write(self, text) : | ||
| def write(self, text): | ||
| self.file1.write(text) | ||
| self.file2.write(text) | ||
| def flush(self) : | ||
| def flush(self): | ||
| self.file1.flush() | ||
| self.file2.flush() | ||
| class BuildCodes(CodeCommand): | ||
@@ -884,3 +860,3 @@ | ||
| user_options = list(CodeCommand.user_options) | ||
| user_options.append( ('clean=', 'c', "clean code",), ) | ||
| user_options.append(('clean=', 'c', "clean code",), ) | ||
@@ -890,11 +866,11 @@ def initialize_options(self): | ||
| self.clean = 'no' | ||
| def finalize_options (self): | ||
| def finalize_options(self): | ||
| CodeCommand.finalize_options(self) | ||
| self.must_clean = self.clean == 'yes' | ||
| self.must_dist_clean = self.clean == 'dist' | ||
| def run_make_on_directory(self, codename, directory, target, environment): | ||
| buildlog = os.path.abspath("build.log") | ||
| with open(buildlog, "a") as output: | ||
@@ -907,3 +883,3 @@ output.write('*'*100) | ||
| output.flush() | ||
| if environment.get('AMUSE_USE_CCACHE', 0) != "1" or "CCACHE_BASEDIR" in environment: | ||
@@ -917,13 +893,13 @@ build_environment = environment | ||
| result, resultcontent = self.call( | ||
| ['make','-C', directory, target], | ||
| ['make', '-C', directory, target], | ||
| output, | ||
| env = build_environment | ||
| env=build_environment | ||
| ) | ||
| with open(buildlog, "a") as output: | ||
| output.write('*'*100) | ||
| output.write('\n') | ||
| return result, resultcontent | ||
| def is_download_needed(self, string): | ||
@@ -934,3 +910,3 @@ for line in string.splitlines(): | ||
| return False | ||
| def is_cuda_needed(self, string): | ||
@@ -943,3 +919,3 @@ for line in string.splitlines(): | ||
| return False | ||
| def are_python_imports_needed(self, string): | ||
@@ -950,4 +926,4 @@ for line in string.splitlines(): | ||
| return False | ||
| def run (self): | ||
| def run(self): | ||
| if self.must_clean: | ||
@@ -967,8 +943,8 @@ self.do_clean() | ||
| environment = self.build_environment() | ||
| buildlog = 'build.log' | ||
| self.announce("building libraries and community codes", level = log.INFO) | ||
| self.announce("build, for logging, see '{0}'".format(buildlog), level = log.INFO) | ||
| self.announce("building libraries and community codes", level=logging.INFO) | ||
| self.announce("build, for logging, see '{0}'".format(buildlog), level=logging.INFO) | ||
| with open(buildlog, "w") as output: | ||
@@ -980,17 +956,17 @@ output.write('*'*100) | ||
| output.write('\n') | ||
| if not self.lib_dir == self.lib_src_dir: | ||
| self.copy_build_prereq_to_build_dir() | ||
| self.copy_lib_to_build_dir() | ||
| for x in self.makefile_paths(self.lib_dir): | ||
| shortname = x[len(self.lib_dir) + 1:] + '-library' | ||
| starttime = datetime.datetime.now() | ||
| self.announce("[{1:%H:%M:%S}] building {0}".format(shortname, starttime), level = log.INFO) | ||
| self.announce("[{1:%H:%M:%S}] building {0}".format(shortname, starttime), level=logging.INFO) | ||
| returncode, outputlog = self.run_make_on_directory(shortname, x, 'all', environment) | ||
| endtime = datetime.datetime.now() | ||
| if returncode == 2: | ||
| self.announce("[{2:%H:%M:%S}] building {0}, failed, see {1!r} for error log".format(shortname, buildlog, endtime), level = log.DEBUG) | ||
| self.announce("[{2:%H:%M:%S}] building {0}, failed, see {1!r} for error log".format(shortname, buildlog, endtime), level=logging.DEBUG) | ||
| if self.is_download_needed(outputlog): | ||
@@ -1003,13 +979,13 @@ is_download_needed.append(x[len(self.lib_dir) + 1:]) | ||
| else: | ||
| self.announce("[{1:%H:%M:%S}] building {0}, succeeded".format(shortname, endtime), level = log.DEBUG) | ||
| self.announce("[{1:%H:%M:%S}] building {0}, succeeded".format(shortname, endtime), level=logging.DEBUG) | ||
| lib_build.append(shortname) | ||
| if not self.codes_dir == self.codes_src_dir: | ||
| self.copy_codes_to_build_dir() | ||
| #environment.update(self.environment) | ||
| # environment.update(self.environment) | ||
| makefile_paths = list(self.makefile_paths(self.codes_dir)) | ||
| build_to_special_targets = {} | ||
| for x in makefile_paths: | ||
@@ -1020,3 +996,3 @@ shortname = x[len(self.codes_dir) + 1:].lower() | ||
| # to distribute mesa, it will make the | ||
| # download size from about 100mb size | ||
| # download size from about 100mb size | ||
| # to > 1Gb size. | ||
@@ -1027,10 +1003,13 @@ # | ||
| if not self.inplace and shortname == 'mesa': | ||
| self.announce("[{1:%H:%M:%S}] skipping {0}".format(shortname, starttime), level = log.INFO) | ||
| continue | ||
| self.announce("[{1:%H:%M:%S}] building {0}".format(shortname, starttime), level = log.INFO) | ||
| self.announce("[{1:%H:%M:%S}] skipping {0}".format(shortname, starttime), level=logging.INFO) | ||
| continue | ||
| self.announce("[{1:%H:%M:%S}] building {0}".format(shortname, starttime), level=logging.INFO) | ||
| returncode, outputlog = self.run_make_on_directory(shortname, x, 'all', environment) | ||
| endtime = datetime.datetime.now() | ||
| if returncode > 0: | ||
| self.announce("[{2:%H:%M:%S}] building {0}, failed, see {1!r} for error log".format(shortname, buildlog, endtime), level = log.DEBUG) | ||
| self.announce( | ||
| "[{2:%H:%M:%S}] building {0}, failed, see {1!r} for error log".format(shortname, buildlog, endtime), | ||
| level=logging.DEBUG | ||
| ) | ||
| if self.is_download_needed(outputlog): | ||
@@ -1044,3 +1023,3 @@ is_download_needed.append(shortname) | ||
| not_build.append(shortname) | ||
| if self.is_mpi_enabled(): | ||
@@ -1051,11 +1030,17 @@ continue | ||
| is_built = True | ||
| self.announce("[{1:%H:%M:%S}] building {0}, succeeded".format(shortname, endtime), level = log.DEBUG) | ||
| self.announce( | ||
| "[{1:%H:%M:%S}] building {0}, succeeded".format(shortname, endtime), | ||
| level=logging.DEBUG | ||
| ) | ||
| if not self.variant: | ||
| continue | ||
| special_targets = self.get_special_targets(shortname, x, environment) | ||
| for target,target_name in special_targets: | ||
| for target, target_name in special_targets: | ||
| starttime = datetime.datetime.now() | ||
| self.announce("[{2:%H:%M:%S}] building {0} - {1}".format(shortname, target_name, starttime), level = log.DEBUG) | ||
| self.announce( | ||
| "[{2:%H:%M:%S}] building {0} - {1}".format(shortname, target_name, starttime), | ||
| level=logging.DEBUG | ||
| ) | ||
| returncode, outputlog = self.run_make_on_directory(shortname, x, target, environment) | ||
@@ -1066,14 +1051,19 @@ endtime = datetime.datetime.now() | ||
| specials_list.append(target_name) | ||
| self.announce("[{3:%H:%M:%S}] building {0} - {1}, failed, see {2!r} for error log".format(shortname, target_name, buildlog,endtime), level = log.DEBUG) | ||
| self.announce( | ||
| "[{3:%H:%M:%S}] building {0} - {1}, failed, see {2!r} for error log".format(shortname, target_name, buildlog, endtime), | ||
| level=logging.DEBUG | ||
| ) | ||
| else: | ||
| build_to_special_targets.setdefault(shortname, list()).append(target_name) | ||
| self.announce("[{2:%H:%M:%S}] building {0} - {1}, succeeded".format(shortname, target_name, endtime), level = log.DEBUG) | ||
| #~ if supportrc["framework_install"]: | ||
| #~ self.copy_config_to_build_dir() | ||
| self.announce( | ||
| "[{2:%H:%M:%S}] building {0} - {1}, succeeded".format(shortname, target_name, endtime), level=logging.DEBUG | ||
| ) | ||
| # if supportrc["framework_install"]: | ||
| # self.copy_config_to_build_dir() | ||
| if not self.codes_dir == self.codes_src_dir: | ||
| #~ self.copy_worker_codes_to_build_dir() | ||
| # self.copy_worker_codes_to_build_dir() | ||
| self.copy_worker_codes() | ||
| with open(buildlog, "a") as output: | ||
@@ -1085,3 +1075,3 @@ output.write('*'*80) | ||
| output.write('\n') | ||
| self.announce("Environment variables") | ||
@@ -1091,4 +1081,4 @@ self.announce("="*80) | ||
| for x in sorted_keys: | ||
| self.announce("%s\t%s" % (x , self.environment[x] )) | ||
| self.announce("%s\t%s" % (x, self.environment[x])) | ||
| if not self.is_mpi_enabled(): | ||
@@ -1099,3 +1089,3 @@ all_build = set(build) | ||
| if x in build_to_special_targets: | ||
| if not x in all_build: | ||
| if x not in all_build: | ||
| build.append(x) | ||
@@ -1106,46 +1096,56 @@ all_build.add(x) | ||
| not_build = not_build_copy | ||
| if not_build or not_build_special or is_download_needed or is_cuda_needed or are_python_imports_needed: | ||
| if ( | ||
| not_build | ||
| or not_build_special | ||
| or is_download_needed | ||
| or is_cuda_needed | ||
| or are_python_imports_needed | ||
| ): | ||
| if not_build: | ||
| level = log.WARN | ||
| level = logging.WARN | ||
| else: | ||
| level = log.INFO | ||
| level = logging.INFO | ||
| if not_build: | ||
| self.announce("Community codes not built (because of errors/ missing libraries):", level = level) | ||
| self.announce("="*80, level = level) | ||
| self.announce( | ||
| "Community codes not built (because of errors/ missing libraries):", | ||
| level=level | ||
| ) | ||
| self.announce("="*80, level=level) | ||
| for x in not_build: | ||
| self.announce(' * {0}'.format(x), level = level) | ||
| self.announce(' * {0}'.format(x), level=level) | ||
| if not_build_special: | ||
| self.announce("Optional builds skipped, need special libraries:", level = level) | ||
| self.announce("Optional builds skipped, need special libraries:", level=level) | ||
| for x in sorted(not_build_special.keys()): | ||
| self.announce(' * {0} - {1}'.format(x, ', '.join(not_build_special[x])), level = level) | ||
| self.announce(' * {0} - {1}'.format(x, ', '.join(not_build_special[x])), level=level) | ||
| if is_cuda_needed: | ||
| self.announce("Optional builds skipped, need CUDA/GPU libraries:", level = level) | ||
| self.announce("Optional builds skipped, need CUDA/GPU libraries:", level=level) | ||
| for x in is_cuda_needed: | ||
| self.announce(' * {0}'.format(x), level = level) | ||
| self.announce(' * {0}'.format(x), level=level) | ||
| if are_python_imports_needed: | ||
| self.announce("Optional builds skipped, need additional python packages:", level = level) | ||
| self.announce("Optional builds skipped, need additional python packages:", level=level) | ||
| for x in are_python_imports_needed: | ||
| self.announce(' * {0}'.format(x), level = level) | ||
| self.announce(' * {0}'.format(x), level=level) | ||
| if is_download_needed: | ||
| self.announce("Optional builds skipped, need separate download", level = level) | ||
| self.announce("Optional builds skipped, need separate download", level=level) | ||
| for x in is_download_needed: | ||
| self.announce(' * {0} , make {0}.code DOWNLOAD_CODES=1'.format(x), level = level) | ||
| self.announce( | ||
| f' * {x} , make {x}.code DOWNLOAD_CODES=1', level=level | ||
| ) | ||
| self.announce("="*80, level = level) | ||
| self.announce("="*80, level=level) | ||
| if build: | ||
| level = log.INFO | ||
| self.announce("Community codes built", level = level) | ||
| self.announce("="*80, level = level) | ||
| level = logging.INFO | ||
| self.announce("Community codes built", level=level) | ||
| self.announce("="*80, level=level) | ||
| for x in build: | ||
| if x in build_to_special_targets: | ||
| y = build_to_special_targets[x] | ||
| self.announce('* {0} ({1})'.format(x,','.join(y)), level = level) | ||
| self.announce('* {0} ({1})'.format(x, ','.join(y)), level=level) | ||
| else: | ||
| self.announce('* {0}'.format(x), level = level) | ||
| self.announce("="*80, level = level) | ||
| level = log.INFO | ||
| self.announce('* {0}'.format(x), level=level) | ||
| self.announce("="*80, level=level) | ||
| level = logging.INFO | ||
| self.announce( | ||
@@ -1158,3 +1158,3 @@ "{0} out of {1} codes built, {2} out of {3} libraries built".format( | ||
| ), | ||
| level = level | ||
| level=level | ||
| ) | ||
@@ -1166,3 +1166,3 @@ self.announce("(not all codes and libraries need to be built)") | ||
| "Your configuration is out of date, please rerun configure", | ||
| level = level | ||
| level=level | ||
| ) | ||
@@ -1217,3 +1217,3 @@ | ||
| BuildCodes.initialize_options(self) | ||
| self.inplace=True | ||
| self.inplace = True | ||
@@ -1225,9 +1225,8 @@ | ||
| def run (self): | ||
| def run(self): | ||
| if os.path.exists('config.mk') or self.config: | ||
| self.announce("Already configured, not running configure", level = 2) | ||
| self.announce("Already configured, not running configure", level=2) | ||
| return | ||
| environment = self.build_environment() | ||
| self.announce("Running configure for AMUSE", level = 2) | ||
| self.announce("Running configure for AMUSE", level=2) | ||
| result,content=self.call(['./configure'], env=environment, shell=True) | ||
@@ -1245,3 +1244,3 @@ if not os.path.exists('config.mk'): | ||
| class CleanCodes(CodeCommand): | ||
@@ -1251,8 +1250,7 @@ | ||
| def run (self): | ||
| self.announce("Cleaning libraries and community codes", level = 2) | ||
| def run(self): | ||
| self.announce("Cleaning libraries and community codes", level=2) | ||
| self.do_clean() | ||
| class DistCleanCodes(CodeCommand): | ||
@@ -1262,12 +1260,12 @@ | ||
| def run (self): | ||
| self.announce("Cleaning for distribution, libraries and community codes", level = 2) | ||
| def run(self): | ||
| self.announce("Cleaning for distribution, libraries and community codes", level=2) | ||
| self.do_distclean() | ||
| class BuildOneCode(BuildCodes): | ||
| class BuildOneCode(BuildCodes): | ||
| description = "build one code" | ||
| user_options = list(BuildCodes.user_options) | ||
| user_options.append( ('code-name=', 'n', "name of the code",), ) | ||
| user_options.append(('code-name=', 'n', "name of the code",), ) | ||
@@ -1277,4 +1275,4 @@ def initialize_options(self): | ||
| self.code_name = None | ||
| def finalize_options (self): | ||
| def finalize_options(self): | ||
| BuildCodes.finalize_options(self) | ||
@@ -1287,3 +1285,3 @@ if self.code_name is None: | ||
| return | ||
| names = os.listdir(path) | ||
@@ -1302,3 +1300,3 @@ for name in names: | ||
| self.run_command("build_py") | ||
| BuildCodes.run(self) | ||
@@ -1308,3 +1306,3 @@ | ||
| # make sure sub_commands are independent | ||
| sub_commands=list(clean.sub_commands) | ||
| sub_commands = list(clean.sub_commands) | ||
@@ -1316,4 +1314,4 @@ def run(self): | ||
| class Install(install): | ||
| sub_commands=list(install.sub_commands) | ||
| sub_commands = list(install.sub_commands) | ||
| def run(self): | ||
@@ -1328,3 +1326,3 @@ # this ensures sub commands are run first (only run once) | ||
| sub_commands=list(develop.sub_commands) | ||
| sub_commands = list(develop.sub_commands) | ||
@@ -1340,8 +1338,8 @@ def run(self): | ||
| sub_commands=list(develop.sub_commands) | ||
| sub_commands = list(develop.sub_commands) | ||
| def run(self): | ||
| build.sub_commands.remove( ('build_codes', None) ) | ||
| build.sub_commands.append( ('build_libraries_in_place', None) ) | ||
| build.sub_commands.remove(('build_codes', None)) | ||
| build.sub_commands.append(('build_libraries_in_place', None)) | ||
| # this ensures sub commands are run first (only run once) | ||
@@ -1365,7 +1363,7 @@ for cmd_name in self.get_sub_commands(): | ||
| 'install_libraries': InstallLibraries, | ||
| 'develop' : Develop, | ||
| 'develop_build' : BuildCodes_inplace, | ||
| 'editable_wheel' : Editable_wheel | ||
| 'develop': Develop, | ||
| 'develop_build': BuildCodes_inplace, | ||
| 'editable_wheel': Editable_wheel | ||
| } | ||
| build.sub_commands.append(('build_codes', None)) | ||
@@ -1376,3 +1374,3 @@ Clean.sub_commands.append(('clean_codes', None)) | ||
| Develop.sub_commands.append(('build_libraries_in_place', None)) | ||
| if supportrc["framework_install"]: | ||
@@ -1390,4 +1388,2 @@ mapping_from_command_name_to_command_class.update( | ||
| return mapping_from_command_name_to_command_class |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
5144
0.47%32049746
0