import os,sys,glob,shutil,datetime
import numpy
from .Library_LibraryDependencyList import *
from .Library_FileWriteText import *
from .Library_FileReadAsText import *
__filedir__=os.path.abspath(os.path.dirname(__file__))
[docs]def create_package(
PackageReadme= True,
PackageTargetDirectory= ".",
PackageSourceDirectory= ".",
PackageName= "",
PackageAuthor= "",
PackageDataFileTypes= [],
PackageDataFolders=[],
PackageAuthorEmail= "",
PackageInstallRequires= [],
IncludeInAPI=[],
PackageLicense= "",
PackageURL= "",
PackageTests= True,
CreateDocs=True,
InitAsGit= False,
ConnectRemoteGitRepository= '',
CheckArguments = False,
PrintExtra = False,
):
"""
Used to create a python package skeleton, should come out of the box with
some bare documentation and be installable/ready to be pip installable.
Parameters
----------
PackageReadme: bool
If True, a barebones README is created in the base package directory.
PackageTargetDirectory: str
The path/name of the save location for your package
PackageSourceDirectory: str
The path/name of the folder containing your code
PackageName: str
What you want the name of your package to be
PackageAuthor: str
Your name (for docs)
PackageDataFileTypes: list
List of data file extensions (e.g. txt, dat) for any data files in your code directory
PackageDataFolders: list
List of data folders you need included in your package
PackageAuthorEmail: str
Author e-mail address for docs
PackageInstallRequires: list
By default this package will guess what remote dependencies exist from the code itself,
this will overwrite that.
IncludeInAPI: list
List of your modules you want to run autodoc on (and therefore should be documented so that
autodoc can read them)
PackageLicense: str
Your package license (for docs)
PackageURL: str
If your package has a website (for docs)
PackageTests: bool
If True, a test script is set up for your package (that you should then edit.)
CreateDocs: bool
If True, barebones documentation is created for a readthedocs page.
InitAsGit: bool
If True, the new package directory is initialized as a git repository
ConnectRemoteGitRepository: str
A remote git repository to connect your local repo to, and adds it to docs install
CheckArguments: bool
if true, checks the arguments with conditions written in the function
if false, ignores those conditions
PrintExtra: int
if greater than 0, prints addional information about the function
if 0, function is expected to print nothing to console
Returns
-------
"""
Result = None
if (CheckArguments):
ArgumentErrorMessage = ""
if len(PackageName) == 0:
ArgumentErrorMessage += "Must supply a name of your package.\n"
if not isinstance(PackageDataFileTypes,(list,tuple,numpy.ndarray)):
PackageDataFileTypes = [PackageDataFileTypes]
if not isinstance(PackageInstallRequires,(list,tuple,numpy.ndarray)):
PackageInstallRequires = [PackageInstallRequires]
if CreateDocs:
import sphinx_rtd_theme
if (len(ArgumentErrorMessage) > 0 ):
if(PrintExtra):
print("ArgumentErrorMessage:\n", ArgumentErrorMessage)
raise Exception(ArgumentErrorMessage)
if os.path.exists(PackageTargetDirectory):
raise Exception("Package directory %s already exists!!"%PackageTargetDirectory)
os.makedirs(PackageTargetDirectory)
ProjectPythonFiles = glob.glob(os.path.join(PackageSourceDirectory,'*.py'))
ProjectDataFiles = list(numpy.array([glob.glob(os.path.join(PackageSourceDirectory,'*.%s'%fileExtension)) for fileExtension in PackageDataFileTypes]).flatten())
ProjectPythonFileBasenamesWithoutExtension = [os.path.splitext(os.path.basename(PythonFile))[0] for PythonFile in ProjectPythonFiles]
ProjectPackageLocalDependencies = []
ProjectPackageRemoteDependencies = []
ProjectPackageBuiltinDependencies = []
ProjectPackageMissingDependencies = []
ExtraBuiltins = ['traceback','copy','pickle','datetime','os','math','types','random','shutil',
'pylab','inspect','socket','re','multiprocessing','subprocess','glob']
for ProjectFileName in ProjectPythonFiles:
LibraryName = os.path.splitext(ProjectFileName)[0]
List = Library_LibraryDependencyList(LibraryName = LibraryName,PrintExtra=PrintExtra)
for ModuleOrPackage in List:
if len(ModuleOrPackage) == 0 or ModuleOrPackage[0] == '#':
continue
if ModuleOrPackage[0] == '.': #This is a relative import
ModuleOrPackage = ModuleOrPackage[1:]
ModuleOrPackage = ModuleOrPackage.split('.')[0]
if numpy.any([ModuleOrPackage in PotentialPackageList for PotentialPackageList in [ProjectPackageRemoteDependencies,
ProjectPackageLocalDependencies,
ProjectPackageMissingDependencies,
ProjectPackageBuiltinDependencies]]):
#We've already found a place for this Module or Package
continue
if PrintExtra:
print('CHECKING:%s'%ModuleOrPackage)
if ModuleOrPackage in ProjectPythonFileBasenamesWithoutExtension: #Must be a local dependency
ProjectPackageLocalDependencies.append(ModuleOrPackage)
elif ModuleOrPackage in sys.builtin_module_names or '_'+ModuleOrPackage in sys.builtin_module_names or ModuleOrPackage in ExtraBuiltins or len(PackageInstallRequires)>0:
ProjectPackageBuiltinDependencies.append(ModuleOrPackage)
elif os.system('pip search %s > /dev/null 2>&1'%ModuleOrPackage)==0 or os.system('conda search %s > /dev/null 2>&1'%ModuleOrPackage)==0: #in either pip or conda
ProjectPackageRemoteDependencies.append(ModuleOrPackage)
else:
ProjectPackageMissingDependencies.append(ModuleOrPackage)
if len(PackageInstallRequires) > 0:
ProjectPackageRemoteDependencies = PackageInstallRequires
if PrintExtra and len(ProjectPackageMissingDependencies) > 0:
print('Warning: Did not find the following modules/packages, hopefully you know why...{}'.format(ProjectPackageMissingDependencies))
PackageSetupPackages = 'numpy' if 'numpy' in ProjectPackageRemoteDependencies else ''
if PackageTests and 'astropy' not in ProjectPackageRemoteDependencies:
ProjectPackageRemoteDependencies.append('astropy')
if PackageTests and 'pytest-astropy' not in ProjectPackageRemoteDependencies:
ProjectPackageRemoteDependencies.append('pytest-astropy')
#Start creating all these files/folders...
os.makedirs(os.path.join(PackageTargetDirectory,PackageName))
if len(PackageDataFileTypes) == 0:
PackageData = False
else:
PackageData = True
SetupFileContents=Library_FileReadAsText(os.path.join(__filedir__,'default','setup.py'))
SetupFileContents=SetupFileContents.replace('PACKAGE_NAME',PackageName)
SetupFileContents=SetupFileContents.replace('PACKAGE_AUTHOR_EMAIL',PackageAuthorEmail)
SetupFileContents=SetupFileContents.replace('PACKAGE_AUTHOR_NAME',PackageAuthor)
SetupFileContents=SetupFileContents.replace('PACKAGE_URL',PackageURL)
SetupFileContents=SetupFileContents.replace('PACKAGE_LICENSE',PackageLicense)
SetupFileContents=SetupFileContents.replace('PACKAGE_INSTALL','{}'.format(ProjectPackageRemoteDependencies))
SetupFileContents=SetupFileContents.replace('PACKAGE_SETUP',PackageSetupPackages)
if len(ProjectDataFiles)>0:
SetupFileContents=SetupFileContents.replace('DATA_FOLDERS','{}'.format(list(numpy.append(PackageDataFolders,'package_data'))))
else:
SetupFileContents=SetupFileContents.replace('DATA_FOLDERS','{}'.format(PackageDataFolders))
ModuleWriteResult = Library_FileWriteText(
Filepath = os.path.join(PackageTargetDirectory,'setup.py'),
WriteText = SetupFileContents,
OverWrite = True,
PrintExtra=PrintExtra
)
if CreateDocs:
shutil.copytree(os.path.join(__filedir__,'default','docs'),os.path.join(PackageTargetDirectory,'Docs'))
DocsConfigFileContents = Library_FileReadAsText(os.path.join(PackageTargetDirectory,'Docs','source','conf.py'))
DocsConfigFileContents = DocsConfigFileContents.replace('PROJECT_NAME',PackageName)
DocsConfigFileContents = DocsConfigFileContents.replace('PACKAGE_COPYRIGHT',str(int(datetime.datetime.now().year))+' '+PackageAuthor)
DocsConfigFileContents = DocsConfigFileContents.replace('PACKAGE_AUTHOR_NAME',PackageAuthor)
ModuleWriteResult = Library_FileWriteText(
Filepath = os.path.join(PackageTargetDirectory,'Docs','source','conf.py'),
WriteText = DocsConfigFileContents,
OverWrite = True,
PrintExtra=PrintExtra
)
DocsSourceIndex=Library_FileReadAsText(os.path.join(PackageTargetDirectory,'Docs','source','index.rst'))
DocsSourceIndex=DocsSourceIndex.replace('PACKAGE_NAME',PackageName)
DocsSourceIndex=DocsSourceIndex.replace('EQUAL_SIGNS','='*(len('Welcome to ')+len(PackageName)))
ModuleWriteResult = Library_FileWriteText(
Filepath = os.path.join(PackageTargetDirectory,'Docs','source','index.rst'),
WriteText = DocsSourceIndex,
OverWrite = True,PrintExtra=PrintExtra
)
if len(IncludeInAPI)>0:
DocsSourceAPI = ''
DocsSourceAPI += '#################\n'
DocsSourceAPI += 'API Documentation\n'
DocsSourceAPI += '#################\n'
DocsSourceAPI += '\n'
DocsSourceAPI += '|\n'
DocsSourceAPI += '\n'
DocsSourceAPI += '*'*len(PackageName)+'\n'
DocsSourceAPI += '%s\n'%PackageName
DocsSourceAPI += '*'*len(PackageName)+'\n'
DocsSourceAPI += '\n'
for pythonFile in IncludeInAPI:
DocsSourceAPI += '-'*len(pythonFile)+'\n'
DocsSourceAPI += '%s\n'%pythonFile
DocsSourceAPI += '-'*len(pythonFile)+'\n'
DocsSourceAPI += '.. automodule:: %s.%s\n'%(PackageName,pythonFile)
DocsSourceAPI += ' :members:\n'
DocsSourceAPI += '\n'
DocsSourceAPI += '|\n'
DocsSourceAPI += '\n'
ModuleWriteResult = Library_FileWriteText(
Filepath = os.path.join(PackageTargetDirectory,'Docs','source','api.rst'),
WriteText = DocsSourceAPI,
OverWrite = True,
PrintExtra=PrintExtra
)
DocsSourceInstall=Library_FileReadAsText(os.path.join(PackageTargetDirectory,'Docs','source','install.rst'))
DocsSourceInstall = DocsSourceInstall.replace('PACKAGE_NAME',PackageName)
if not InitAsGit or len(ConnectRemoteGitRepository) == 0:
ind=DocsSourceInstall.find('Install latest development version')
DocsSourceInstall=DocsSourceInstall[:ind]
else:
DocsSourceInstall=DocsSourceInstall.replace('GIT_REPOSITORY',ConnectRemoteGitRepository)
if not PackageTests:
DocsSourceInstall=DocsSourceInstall.replace('python setup.py test\n','')
ModuleWriteResult = Library_FileWriteText(
Filepath = os.path.join(PackageTargetDirectory,'Docs','source','install.rst'),
WriteText = DocsSourceInstall,
OverWrite = True,PrintExtra=PrintExtra
)
DocsExample=Library_FileReadAsText(os.path.join(PackageTargetDirectory,'Docs','source','_examples','plot_package.py'))
DocsExample = DocsExample.replace('PACKAGE_NAME',PackageName)
ModuleWriteResult = Library_FileWriteText(
Filepath = os.path.join(PackageTargetDirectory,'Docs','source','_examples','plot_package.py'),
WriteText = DocsExample,
OverWrite = True,PrintExtra=PrintExtra
)
shutil.copyfile(os.path.join(__filedir__,'default','.readthedocs.yml'),os.path.join(PackageTargetDirectory,'.readthedocs.yml'))
pip_requirements=Library_FileReadAsText(os.path.join(PackageTargetDirectory,'Docs','source','requirements.txt'))
if isinstance(PackageSetupPackages,list):
pip_requirements=pip_requirements.replace('SETUP_REQUIRES','\n'.join(PackageSetupPackages))
ModuleWriteResult = Library_FileWriteText(
Filepath = os.path.join(PackageTargetDirectory,'Docs','source','requirements.txt'),
WriteText = pip_requirements,
OverWrite = True,PrintExtra=PrintExtra
)
pip_requirements2=Library_FileReadAsText(os.path.join(PackageTargetDirectory,'Docs','source','requirements2.txt'))
pip_requirements2=pip_requirements2.replace('INSTALL_REQUIRES','\n'.join(ProjectPackageRemoteDependencies))
ModuleWriteResult = Library_FileWriteText(
Filepath = os.path.join(PackageTargetDirectory,'Docs','source','requirements2.txt'),
WriteText = pip_requirements2,
OverWrite = True,PrintExtra=PrintExtra
)
if InitAsGit:
PackageGitIgnore=Library_FileReadAsText(os.path.join(__filedir__,'default','.gitignore'))
PackageGitIgnore=PackageGitIgnore.replace('PACKAGE_NAME',PackageName)
ModuleWriteResult = Library_FileWriteText(
Filepath = os.path.join(PackageTargetDirectory,'.gitignore'),
WriteText = PackageGitIgnore,
OverWrite = True,PrintExtra=PrintExtra
)
if PackageReadme:
ModuleWriteResult = Library_FileWriteText(
Filepath = os.path.join(PackageTargetDirectory,'README.md'),
WriteText = '#Insert info about %s here!\n'%PackageName,
OverWrite = True,PrintExtra=PrintExtra
)
PackageInitFile=Library_FileReadAsText(os.path.join(__filedir__,'default','__init__.py'))
importString=''
for pythonFile in ProjectPythonFiles:
importString += 'from .%s import *\n'%os.path.splitext(os.path.basename(pythonFile))[0]
PackageInitFile=PackageInitFile.replace('IMPORT_LIST',importString)
if not PackageTests:
ind=PackageInitFile.find('def')
PackageInitFile=PackageInitFile[:ind]
ModuleWriteResult = Library_FileWriteText(
Filepath = os.path.join(PackageTargetDirectory,PackageName,'__init__.py'),
WriteText = PackageInitFile,
OverWrite = True,PrintExtra=PrintExtra
)
#Copy over files
for PythonFile in ProjectPythonFiles:
if os.path.basename(PythonFile).startswith('Example'):
if not os.path.exists(os.path.join(PackageTargetDirectory,PackageName,'examples')):
os.makedirs(os.path.join(PackageTargetDirectory,PackageName,'examples'))
shutil.copyfile(PythonFile,os.path.join(PackageTargetDirectory,PackageName,'examples',os.path.basename(PythonFile)))
elif os.path.basename(PythonFile).startswith('Test'):
if not os.path.exists(os.path.join(PackageTargetDirectory,PackageName,'tests')):
os.makedirs(os.path.join(PackageTargetDirectory,PackageName,'tests'))
shutil.copyfile(PythonFile,os.path.join(PackageTargetDirectory,PackageName,'tests',os.path.basename(PythonFile)))
else:
shutil.copyfile(PythonFile,os.path.join(PackageTargetDirectory,PackageName,os.path.basename(PythonFile)))
for DataFile in ProjectDataFiles:
if not os.path.exists(os.path.join(PackageTargetDirectory,PackageName,'package_data')):
os.makedirs(os.path.join(PackageTargetDirectory,PackageName,'package_data'))
shutil.copyfile(DataFile,os.path.join(PackageTargetDirectory,PackageName,'package_data',os.path.basename(DataFile)))
for DataFolder in PackageDataFolders:
shutil.copytree(os.path.join(PackageSourceDirectory,DataFolder),os.path.join(PackageTargetDirectory,PackageName,DataFolder))
#Create relative imports
FilesToMakeRelative = glob.glob(os.path.join(PackageTargetDirectory,PackageName,'*.py'))
for PythonFile in [os.path.splitext(f)[0] for f in FilesToMakeRelative]:
if '__init__' in PythonFile:
continue
LocalDependencies = Library_LibraryDependencyList(PythonFile,PrintExtra=PrintExtra)
PythonFileContent = Library_FileReadAsText(PythonFile+'.py')
for NeedsToBeRelative in LocalDependencies:
if NeedsToBeRelative not in ProjectPackageLocalDependencies:
continue
if PythonFileContent.find('import %s'%NeedsToBeRelative)==-1: #must be a from __ import __ case
regular = False
else:
regular = True
if regular:
PythonFileContent = PythonFileContent.replace('import %s'%NeedsToBeRelative,'from .%s import *'%NeedsToBeRelative)
else:
PythonFileContent = PythonFileContent.replace('from %s'%NeedsToBeRelative,'from .%s'%NeedsToBeRelative)
if '%s.Main'%NeedsToBeRelative in PythonFileContent:
PythonFileContent = PythonFileContent.replace('%s.Main'%NeedsToBeRelative,NeedsToBeRelative)
else:
PythonFileContent = PythonFileContent.replace('%s.'%NeedsToBeRelative,'')
PythonFileContent = PythonFileContent.replace('def create_package','def %s'%os.path.basename(PythonFile))
for f in ProjectDataFiles:
PythonFileContent = PythonFileContent.replace(f,os.path.join('package_data',os.path.basename(f)))
ModuleWriteResult = Library_FileWriteText(
Filepath = PythonFile+'.py',
WriteText = PythonFileContent,
OverWrite = True, PrintExtra=PrintExtra
)
#Generate Package Test File
if PackageTests:
PackageTestFile = ''
PackageTestFile += '##Test File\n'
PackageTestFile += 'import sys,os,traceback\n'
PackageTestFile += 'from copy import deepcopy\n'
PackageTestFile += "sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),'..'))\n"
PackageTestFile += 'import %s\n'%PackageName
PackageTestFile += '\n'
PackageTestFile += 'def test_%s():\n'%PackageName
PackageTestFile += '\tfailed=0\n'
PackageTestFile += '\ttotal=0\n'
PackageTestFile += '\t#Fill in tests here.\n'
PackageTestFile += '\ttry: \n'
PackageTestFile += '\t\ttotal+=1 \n'
PackageTestFile += "\t\tprint('Testing package...',end='')\n"
PackageTestFile += "\t\tprint('Passed!')\n"
PackageTestFile += '\texcept Exception as e:\n'
PackageTestFile += "\t\tprint('Failed')\n"
PackageTestFile += '\t\tprint(traceback.format_exc())\n'
PackageTestFile += '\t\t\n'
PackageTestFile += '\t\tfailed+=1\n'
PackageTestFile += '\n'
PackageTestFile += "\tprint('Passed %i/%i tests.'%(total-failed,total))\n"
PackageTestFile += '\n'
PackageTestFile += '\treturn\n'
PackageTestFile += '\n'
PackageTestFile += "if __name__ == '__main__':\n"
PackageTestFile += '\ttest_%s()\n'%PackageName
ModuleWriteResult = Library_FileWriteText(
Filepath = os.path.join(PackageTargetDirectory,PackageName,'test_%s.py'%PackageName),
WriteText = PackageTestFile,
OverWrite = True,PrintExtra=PrintExtra
)
if InitAsGit:
os.system('git --git-dir %s init'%os.path.join(PackageTargetDirectory,'.git'))
if len(ConnectRemoteGitRepository)>0:
os.system('git -C %s remote add origin %s'%(os.path.join(PackageTargetDirectory),ConnectRemoteGitRepository))
os.system('git -C pull')
os.system('git -C %s add .'%(os.path.join(PackageTargetDirectory)))
os.system('git -C %s commit -m "First commit."'%os.path.join(PackageTargetDirectory))
try:
os.system('git -C %s push -u origin master'%os.path.join(PackageTargetDirectory))
except:
os.system('git -C %s push origin master'%os.path.join(PackageTargetDirectory))
return Result