setup.py

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

eric ide

mercurial