setup.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 9198
93d2cfc8d6aa
child 9264
18a7312cfdb3
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 #!/usr/bin/env python3 1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 3
4 # Copyright (c) 2019 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> 4 # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
5 # 5 #
6 6
7 """ 7 """
8 Module to prepare a distribution package for uploading to PyPI. 8 Minimum module to allow 'pip' to perform editable installs.
9 """ 9 """
10 10
11 import os 11 from setuptools import setup
12 import sys
13 import subprocess # secok
14 import shutil
15 import fnmatch
16 import datetime
17 import json
18 import contextlib
19 12
20 from setuptools import setup, find_packages 13 setup()
21
22 installInfoName = "eric7installpip.json"
23
24 ######################################################################
25 ## some helper functions below
26 ######################################################################
27
28
29 def getVersion():
30 """
31 Function to get the version from file.
32
33 @return string containing the version
34 @rtype str
35 """
36 version = "<unknown>"
37 if sys.argv[-1].startswith(("1", "2")):
38 # assume it is a version info starting with year
39 version = sys.argv[-1]
40 del sys.argv[-1]
41 else:
42 # calculate according our version scheme (year.month)
43 today = datetime.date.today()
44 version = "{0}.{1}".format(today.year - 2000, today.month)
45 return version
46
47
48 def getPackageData(package, extensions):
49 """
50 Function to return data files of a package with given extensions.
51
52 @param package name of the package directory
53 @type str
54 @param extensions list of extensions to test for
55 @type list of str
56 @return list of package data files
57 @rtype list of str
58 """
59 filesList = []
60 for dirpath, _dirnames, filenames in os.walk(package):
61 for fname in filenames:
62 if (
63 not fname.startswith('.') and
64 os.path.splitext(fname)[1] in extensions
65 ):
66 filesList.append(
67 os.path.relpath(os.path.join(dirpath, fname), package))
68 return filesList
69
70
71 def getLongDescription():
72 """
73 Function to get the long description via the README file.
74
75 @return long description
76 @rtype str
77 """
78 with open(os.path.join(os.path.dirname(__file__), "docs", "README.rst"),
79 "r") as f:
80 longDescription = f.read()
81
82 if not longDescription:
83 longDescription = (
84 "eric is an integrated development environment for the Python"
85 " programming language. It uses the PyQt6 bindings and the"
86 " QScintilla2 editor widget. See"
87 " https://eric-ide.python-projects.org for more details."
88 )
89
90 return longDescription
91
92 ######################################################################
93 ## functions to prepare the sources for building
94 ######################################################################
95
96
97 def prepareInfoFile(fileName, version):
98 """
99 Function to prepare an Info.py file.
100
101 @param fileName name of the Python file containing the info (string)
102 @param version version string for the package (string)
103 """
104 if not fileName:
105 return
106
107 with contextlib.suppress(OSError):
108 os.rename(fileName, fileName + ".orig")
109 try:
110 hgOut = subprocess.run( # secok
111 ["hg", "identify", "-i"], check=True,
112 capture_output=True, text=True
113 ).stdout
114 except (OSError, subprocess.CalledProcessError):
115 hgOut = ""
116 if hgOut:
117 hgOut = hgOut.strip()
118 if hgOut.endswith("+"):
119 hgOut = hgOut[:-1]
120 with open(fileName + ".orig", "r", encoding="utf-8") as f:
121 text = f.read()
122 text = (
123 text.replace("@@REVISION@@", hgOut)
124 .replace("@@VERSION@@", version)
125 )
126 with open(fileName, "w") as f:
127 f.write(text)
128 else:
129 shutil.copy(fileName + ".orig", fileName)
130
131
132 def prepareAppdataFile(fileName, version):
133 """
134 Function to prepare a .appdata.xml file.
135
136 @param fileName name of the .appdata.xml file (string)
137 @param version version string for the package (string)
138 """
139 if not fileName:
140 return
141
142 with contextlib.suppress(OSError):
143 os.rename(fileName, fileName + ".orig")
144 with open(fileName + ".orig", "r", encoding="utf-8") as f:
145 text = f.read()
146 text = (
147 text.replace("@VERSION@", version)
148 .replace("@DATE@", datetime.date.today().isoformat())
149 )
150 with open(fileName, "w") as f:
151 f.write(text)
152
153
154 def cleanupSource(dirName):
155 """
156 Cleanup the sources directory to get rid of leftover files
157 and directories.
158
159 @param dirName name of the directory to prune (string)
160 """
161 # step 1: delete all Ui_*.py files without a corresponding
162 # *.ui file
163 dirListing = os.listdir(dirName)
164 for formName, sourceName in [
165 (f.replace('Ui_', "").replace(".py", ".ui"), f)
166 for f in dirListing if fnmatch.fnmatch(f, "Ui_*.py")]:
167 if not os.path.exists(os.path.join(dirName, formName)):
168 os.remove(os.path.join(dirName, sourceName))
169 if os.path.exists(os.path.join(dirName, sourceName + "c")):
170 os.remove(os.path.join(dirName, sourceName + "c"))
171
172 # step 2: delete the __pycache__ directory and all remaining *.pyc files
173 if os.path.exists(os.path.join(dirName, "__pycache__")):
174 shutil.rmtree(os.path.join(dirName, "__pycache__"))
175 for name in [f for f in os.listdir(dirName)
176 if fnmatch.fnmatch(f, "*.pyc")]:
177 os.remove(os.path.join(dirName, name))
178
179 # step 3: delete *.orig files
180 for name in [f for f in os.listdir(dirName)
181 if fnmatch.fnmatch(f, "*.orig")]:
182 os.remove(os.path.join(dirName, name))
183
184 # step 4: descent into subdirectories and delete them if empty
185 for name in os.listdir(dirName):
186 name = os.path.join(dirName, name)
187 if os.path.isdir(name):
188 cleanupSource(name)
189 if len(os.listdir(name)) == 0:
190 os.rmdir(name)
191
192
193 def __pyName(py_dir, py_file):
194 """
195 Local function to create the Python source file name for the compiled
196 .ui file.
197
198 @param py_dir suggested name of the directory (string)
199 @param py_file suggested name for the compile source file (string)
200 @return tuple of directory name (string) and source file name (string)
201 """
202 return py_dir, "Ui_{0}".format(py_file)
203
204
205 def compileUiFiles(dirName):
206 """
207 Compile the .ui files to Python sources.
208
209 @param dirName name of the directory to compile UI files for (string)
210 """
211 from PyQt6.uic import compileUiDir
212 compileUiDir(dirName, True, __pyName)
213
214
215 def createInstallInfoFile(dirName):
216 """
217 Create a file containing some rudimentary install information.
218
219 @param dirName name of the directory to compile UI files for
220 @type str
221 """
222 global installInfoName
223
224 installInfo = {
225 "sudo": False,
226 "user": "",
227 "exe": "",
228 "argv": "",
229 "install_cwd": "",
230 "eric": "",
231 "virtualenv": False,
232 "installed": False,
233 "installed_on": "",
234 "guessed": False,
235 "edited": False,
236 "pip": True,
237 "remarks": "",
238 "install_cwd_edited": False,
239 "exe_edited": False,
240 "argv_edited": False,
241 "eric_edited": False,
242 }
243 with open(os.path.join(dirName, installInfoName), "w") as infoFile:
244 json.dump(installInfo, infoFile, indent=2)
245
246 ######################################################################
247 ## setup() below
248 ######################################################################
249
250 Version = getVersion()
251 sourceDir = os.path.join(os.path.dirname(__file__), "eric7")
252 infoFileName = os.path.join(sourceDir, "UI", "Info.py")
253 appdataFileName = os.path.join(os.path.dirname(__file__), "linux",
254 "eric7.appdata.xml")
255 if sys.argv[1].startswith("bdist"):
256 # prepare the sources under "eric7" for building the wheel file
257 print("preparing the sources...") # __IGNORE_WARNING_M801__
258 cleanupSource(sourceDir)
259 compileUiFiles(sourceDir)
260 prepareInfoFile(infoFileName, Version)
261 prepareAppdataFile(appdataFileName, Version)
262 createInstallInfoFile(sourceDir)
263 print("Preparation finished") # __IGNORE_WARNING_M801__
264
265 setup(
266 name="eric-ide",
267 version=Version,
268 description="eric-ide is an integrated development environment for the"
269 " Python language.",
270 long_description=getLongDescription(),
271 long_description_content_type="text/x-rst",
272 author="Detlev Offenbach",
273 author_email="detlev@die-offenbachs.de",
274 url="https://eric-ide.python-projects.org",
275 project_urls={
276 "Source Code": "https://hg.die-offenbachs.homelinux.org/eric/",
277 "Issues Tracker": "https://tracker.die-offenbachs.homelinux.org/",
278 "Funding": "https://www.paypal.com/donate/?hosted_button_id=XG3RSPKE3YAJ2",
279 },
280 platforms=["Linux", "Windows", "macOS"],
281 license="GPLv3+",
282 classifiers=[
283 "License :: OSI Approved ::"
284 " GNU General Public License v3 or later (GPLv3+)",
285 "Environment :: MacOS X",
286 "Environment :: Win32 (MS Windows)",
287 "Environment :: X11 Applications",
288 "Environment :: X11 Applications :: Qt",
289 "Intended Audience :: Developers",
290 "Intended Audience :: End Users/Desktop",
291 "Natural Language :: English",
292 "Natural Language :: German",
293 "Natural Language :: Russian",
294 "Natural Language :: Spanish",
295 "Operating System :: MacOS :: MacOS X",
296 "Operating System :: Microsoft :: Windows :: Windows 10",
297 "Operating System :: POSIX :: Linux",
298 "Programming Language :: Python",
299 "Programming Language :: Python :: 3.7",
300 "Programming Language :: Python :: 3.8",
301 "Programming Language :: Python :: 3.9",
302 "Programming Language :: Python :: 3.10",
303 "Programming Language :: Python :: 3.11",
304 "Programming Language :: Python :: Implementation :: CPython",
305 "Topic :: Software Development",
306 "Topic :: Text Editors :: Integrated Development Environments (IDE)"
307 ],
308 keywords="Development PyQt6 IDE Python3",
309 python_requires=">=3.7, <3.12",
310 install_requires=[
311 "pip>=21.1",
312 "wheel",
313 "PyQt6>=6.2.0",
314 "PyQt6-Charts>=6.2.0",
315 "PyQt6-WebEngine>=6.2.0",
316 "PyQt6-QScintilla>=2.13.0",
317 "docutils",
318 "Markdown",
319 "pyyaml",
320 "tomlkit",
321 "chardet",
322 "asttokens",
323 "EditorConfig",
324 "Send2Trash",
325 "Pygments",
326 "parso",
327 "jedi",
328 "packaging",
329 "pipdeptree",
330 "cyclonedx-python-lib",
331 "cyclonedx-bom",
332 "trove-classifiers",
333 "pywin32>=1.0;platform_system=='Windows'",
334 ],
335 packages=find_packages(),
336 zip_safe=False,
337 package_data={
338 "": getPackageData(
339 "eric7",
340 [".png", ".svg", ".svgz", ".xpm", ".ico", ".gif", ".icns", ".txt",
341 ".md", ".rst", ".tmpl", ".html", ".qch", ".css", ".scss", ".qss",
342 ".ehj", ".ethj", ".api", ".bas", ".dat", ".xbel", ".xml", ".js"]
343 ) + ["i18n/eric7_de.qm", "i18n/eric7_en.qm", "i18n/eric7_es.qm",
344 "i18n/eric7_ru.qm",
345 installInfoName,
346 ]
347 },
348 entry_points={
349 "gui_scripts": [
350 "eric7 = eric7.eric7:main",
351 "eric7_browser = eric7.eric7_browser:main",
352 "eric7_compare = eric7.eric7_compare:main",
353 "eric7_configure = eric7.eric7_configure:main",
354 "eric7_diff = eric7.eric7_diff:main",
355 "eric7_editor = eric7.eric7_editor:main",
356 "eric7_hexeditor = eric7.eric7_hexeditor:main",
357 "eric7_iconeditor = eric7.eric7_iconeditor:main",
358 "eric7_plugininstall = eric7.eric7_plugininstall:main",
359 "eric7_pluginrepository = eric7.eric7_pluginrepository:main",
360 "eric7_pluginuninstall = eric7.eric7_pluginuninstall:main",
361 "eric7_qregularexpression = eric7.eric7_qregularexpression:main",
362 "eric7_re = eric7.eric7_re:main",
363 "eric7_shell = eric7.eric7_shell:main",
364 "eric7_snap = eric7.eric7_snap:main",
365 "eric7_sqlbrowser = eric7.eric7_sqlbrowser:main",
366 "eric7_testing = eric7.eric7_testing:main",
367 "eric7_tray = eric7.eric7_tray:main",
368 "eric7_trpreviewer = eric7.eric7_trpreviewer:main",
369 "eric7_uipreviewer = eric7.eric7_uipreviewer:main",
370 "eric7_virtualenv = eric7.eric7_virtualenv:main",
371 ],
372 "console_scripts": [
373 "eric7_api = eric7.eric7_api:main",
374 "eric7_doc = eric7.eric7_doc:main",
375 "eric7_post_install = eric7.eric7_post_install:main",
376 ],
377 },
378 )
379
380 # cleanup
381 for fileName in [infoFileName, appdataFileName]:
382 if os.path.exists(fileName + ".orig"):
383 with contextlib.suppress(OSError):
384 os.remove(fileName)
385 os.rename(fileName + ".orig", fileName)

eric ide

mercurial