Appendix之setup.py:附录文件内容记录setup.py

简介: Appendix之setup.py:附录文件内容记录setup.py


附录文件内容记录setup.py

1. from __future__ import print_function
2. 
3. from distutils.ccompiler import new_compiler as _new_compiler
4. from distutils.command.clean import clean, log
5. from distutils.core import Command
6. from distutils.dir_util import remove_tree
7. from distutils.errors import DistutilsExecError
8. from distutils.msvccompiler import MSVCCompiler
9. from setuptools import setup, find_packages, Extension, Distribution
10. from setuptools.command.build_ext import build_ext
11. from subprocess import Popen, PIPE
12. import argparse
13. import errno
14. import os
15. import platform
16. import re
17. import shlex
18. import sys
19. 
20. # We don't need six...
21. PY3 = sys.version_info[0] >= 3
22. 
23. if PY3:
24. from shlex import quote as shell_quote
25. else:
26. from pipes import quote as shell_quote
27. 
28. try:
29. # This depends on _winreg, which is not availible on not-Windows.
30. from distutils.msvc9compiler import MSVCCompiler as MSVC9Compiler
31. except ImportError:
32.     MSVC9Compiler = None
33. try:
34. from distutils._msvccompiler import MSVCCompiler as MSVC14Compiler
35. except ImportError:
36.     MSVC14Compiler = None
37. 
38. try:
39. from Cython import __version__ as cython_version
40. from Cython.Build import cythonize
41. except ImportError:
42.     cythonize = None
43. else:
44. # We depend upon some features in Cython 0.27; reject older ones.
45. if tuple(map(int, cython_version.split('.'))) < (0, 27):
46. print("Cython {} is too old for PyAV; ignoring it.".format(cython_version))
47.         cythonize = None
48. 
49. 
50. # We will embed this metadata into the package so it can be recalled for debugging.
51. version = open('VERSION.txt').read().strip()
52. try:
53.     git_commit, _ = Popen(['git', 'describe', '--tags'], stdout=PIPE, stderr=PIPE).communicate()
54. except OSError:
55.     git_commit = None
56. else:
57.     git_commit = git_commit.decode().strip()
58. 
59. 
60. _cflag_parser = argparse.ArgumentParser(add_help=False)
61. _cflag_parser.add_argument('-I', dest='include_dirs', action='append')
62. _cflag_parser.add_argument('-L', dest='library_dirs', action='append')
63. _cflag_parser.add_argument('-l', dest='libraries', action='append')
64. _cflag_parser.add_argument('-D', dest='define_macros', action='append')
65. _cflag_parser.add_argument('-R', dest='runtime_library_dirs', action='append')
66. def parse_cflags(raw_cflags):
67.     raw_args = shlex.split(raw_cflags.strip())
68.     args, unknown = _cflag_parser.parse_known_args(raw_args)
69.     config = {k: v or [] for k, v in args.__dict__.items()}
70. for i, x in enumerate(config['define_macros']):
71.         parts = x.split('=', 1)
72.         value = x[1] or None if len(x) == 2 else None
73.         config['define_macros'][i] = (parts[0], value)
74. return config, ' '.join(shell_quote(x) for x in unknown)
75. 
76. def get_library_config(name):
77. """Get distutils-compatible extension extras for the given library.
78. 
79.     This requires ``pkg-config``.
80. 
81.     """
82. try:
83.         proc = Popen(['pkg-config', '--cflags', '--libs', name], stdout=PIPE, stderr=PIPE)
84. except OSError:
85. print('pkg-config is required for building PyAV')
86.         exit(1)
87. 
88.     raw_cflags, err = proc.communicate()
89. if proc.wait():
90. return
91. 
92.     known, unknown = parse_cflags(raw_cflags.decode('utf8'))
93. if unknown:
94. print("pkg-config returned flags we don't understand: {}".format(unknown))
95.         exit(1)
96. 
97. return known
98. 
99. 
100. def update_extend(dst, src):
101. """Update the `dst` with the `src`, extending values where lists.
102. 
103.     Primiarily useful for integrating results from `get_library_config`.
104. 
105.     """
106. for k, v in src.items():
107.         existing = dst.setdefault(k, [])
108. for x in v:
109. if x not in existing:
110.                 existing.append(x)
111. 
112. 
113. def unique_extend(a, *args):
114.     a[:] = list(set().union(a, *args))
115. 
116. 
117. # Obtain the ffmpeg dir from the "--ffmpeg-dir=<dir>" argument
118. # FFMPEG_DIR = None
119. FFMPEG_DIR = 'D://Program Files//ffmpeg'
120. for i, arg in enumerate(sys.argv):
121. if arg.startswith('--ffmpeg-dir='):
122.         FFMPEG_DIR = arg.split('=')[1]
123. break
124. 
125. if FFMPEG_DIR is not None:
126. # delete the --ffmpeg-dir arg so that distutils does not see it
127. del sys.argv[i]
128. if not os.path.isdir(FFMPEG_DIR):
129. print('The specified ffmpeg directory does not exist')
130.         exit(1)
131. else:
132. # Check the environment variable FFMPEG_DIR
133.     FFMPEG_DIR = os.environ.get('FFMPEG_DIR')
134. if FFMPEG_DIR is not None:
135. if not os.path.isdir(FFMPEG_DIR):
136.             FFMPEG_DIR = None
137. 
138. if FFMPEG_DIR is not None:
139.     ffmpeg_lib = os.path.join(FFMPEG_DIR, 'lib')
140.     ffmpeg_include = os.path.join(FFMPEG_DIR, 'include')
141. if os.path.exists(ffmpeg_lib):
142.         ffmpeg_lib = [ffmpeg_lib]
143. else:
144.         ffmpeg_lib = [FFMPEG_DIR]
145. if os.path.exists(ffmpeg_include):
146.         ffmpeg_include = [ffmpeg_include]
147. else:
148.         ffmpeg_include = [FFMPEG_DIR]
149. else:
150.     ffmpeg_lib = []
151.     ffmpeg_include = []
152. 
153. 
154. # The "extras" to be supplied to every one of our modules.
155. # This is expanded heavily by the `config` command.
156. extension_extra = {
157. 'include_dirs': ['include'] + ffmpeg_include,  # The first are PyAV's includes.
158. 'libraries'   : [],
159. 'library_dirs': ffmpeg_lib,
160. }
161. 
162. # The macros which describe the current PyAV version.
163. config_macros = {
164. "PYAV_VERSION": version,
165. "PYAV_VERSION_STR": '"%s"' % version,
166. "PYAV_COMMIT_STR": '"%s"' % (git_commit or 'unknown-commit'),
167. }
168. 
169. 
170. def dump_config():
171. """Print out all the config information we have so far (for debugging)."""
172. print('PyAV:', version, git_commit or '(unknown commit)')
173. print('Python:', sys.version.encode('unicode_escape' if PY3 else 'string-escape'))
174. print('platform:', platform.platform())
175. print('extension_extra:')
176. for k, vs in extension_extra.items():
177. print('\t%s: %s' % (k, [x.encode('utf8') for x in vs]))
178. print('config_macros:')
179. for x in sorted(config_macros.items()):
180. print('\t%s=%s' % x)
181. 
182. 
183. # Monkey-patch for CCompiler to be silent.
184. def _CCompiler_spawn_silent(cmd, dry_run=None):
185. """Spawn a process, and eat the stdio."""
186.     proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
187.     out, err = proc.communicate()
188. if proc.returncode:
189. raise DistutilsExecError(err)
190. 
191. def new_compiler(*args, **kwargs):
192. """Create a C compiler.
193. 
194.     :param bool silent: Eat all stdio? Defaults to ``True``.
195. 
196.     All other arguments passed to ``distutils.ccompiler.new_compiler``.
197. 
198.     """
199.     make_silent = kwargs.pop('silent', True)
200.     cc = _new_compiler(*args, **kwargs)
201. # If MSVC10, initialize the compiler here and add /MANIFEST to linker flags.
202. # See Python issue 4431 (https://bugs.python.org/issue4431)
203. if is_msvc(cc):
204. from distutils.msvc9compiler import get_build_version
205. if get_build_version() == 10:
206.             cc.initialize()
207. for ldflags in [cc.ldflags_shared, cc.ldflags_shared_debug]:
208.                 unique_extend(ldflags, ['/MANIFEST'])
209. # If MSVC14, do not silence. As msvc14 requires some custom
210. # steps before the process is spawned, we can't monkey-patch this.
211. elif get_build_version() == 14:
212.             make_silent = False
213. # monkey-patch compiler to suppress stdout and stderr.
214. if make_silent:
215.         cc.spawn = _CCompiler_spawn_silent
216. return cc
217. 
218. 
219. _msvc_classes = tuple(filter(None, (MSVCCompiler, MSVC9Compiler, MSVC14Compiler)))
220. def is_msvc(cc=None):
221.     cc = _new_compiler() if cc is None else cc
222. return isinstance(cc, _msvc_classes)
223. 
224. 
225. if os.name == 'nt':
226. 
227. if is_msvc():
228.         config_macros['inline'] = '__inline'
229. 
230. # Since we're shipping a self contained unit on Windows, we need to mark
231. # the package as such. On other systems, let it be universal.
232. class BinaryDistribution(Distribution):
233. def is_pure(self):
234. return False
235. 
236.     distclass = BinaryDistribution
237. 
238. else:
239. 
240. # Nothing to see here.
241.     distclass = Distribution
242. 
243. 
244. # Monkey-patch Cython to not overwrite embedded signatures.
245. if cythonize:
246. 
247. from Cython.Compiler.AutoDocTransforms import EmbedSignature
248. 
249.     old_embed_signature = EmbedSignature._embed_signature
250. def new_embed_signature(self, sig, doc):
251. 
252. # Strip any `self` parameters from the front.
253.         sig = re.sub(r'\(self(,\s+)?', '(', sig)
254. 
255. # If they both start with the same signature; skip it.
256. if sig and doc:
257.             new_name = sig.split('(')[0].strip()
258.             old_name = doc.split('(')[0].strip()
259. if new_name == old_name:
260. return doc
261. if new_name.endswith('.' + old_name):
262. return doc
263. 
264. return old_embed_signature(self, sig, doc)
265. 
266.     EmbedSignature._embed_signature = new_embed_signature
267. 
268. 
269. # Construct the modules that we find in the "av" directory.
270. ext_modules = []
271. for dirname, dirnames, filenames in os.walk('av'):
272. for filename in filenames:
273. 
274. # We are looing for Cython sources.
275. if filename.startswith('.') or os.path.splitext(filename)[1] != '.pyx':
276. continue
277. 
278.         pyx_path = os.path.join(dirname, filename)
279.         base = os.path.splitext(pyx_path)[0]
280. 
281. # Need to be a little careful because Windows will accept / or \
282. # (where os.sep will be \ on Windows).
283.         mod_name = base.replace('/', '.').replace(os.sep, '.')
284. 
285.         c_path = os.path.join('src', base + '.c')
286. 
287. # We go with the C sources if Cython is not installed, and fail if
288. # those also don't exist. We can't `cythonize` here though, since the
289. # `pyav/include.h` must be generated (by `build_ext`) first.
290. if not cythonize and not os.path.exists(c_path):
291. print('Cython is required to build PyAV from raw sources.')
292. print('Please `pip install Cython`.')
293.             exit(3)
294.         ext_modules.append(Extension(
295.             mod_name,
296.             sources=[c_path if not cythonize else pyx_path],
297.         ))
298. 
299. 
300. class ConfigCommand(Command):
301. 
302.     user_options = [
303.         ('no-pkg-config', None,
304. "do not use pkg-config to configure dependencies"),
305.         ('compiler=', 'c',
306. "specify the compiler type"), ]
307. 
308.     boolean_options = ['no-pkg-config']
309. 
310. def initialize_options(self):
311.         self.compiler = None
312.         self.no_pkg_config = None
313. 
314. def finalize_options(self):
315.         self.set_undefined_options('build',
316.             ('compiler', 'compiler'),)
317.         self.set_undefined_options('build_ext',
318.             ('no_pkg_config', 'no_pkg_config'),)
319. 
320. def run(self):
321. 
322. # For some reason we get the feeling that CFLAGS is not respected, so we parse
323. # it here. TODO: Leave any arguments that we can't figure out.
324. for name in 'CFLAGS', 'LDFLAGS':
325.             known, unknown = parse_cflags(os.environ.pop(name, ''))
326. if unknown:
327. print("Warning: We don't understand some of {} (and will leave it in the envvar): {}".format(name, unknown))
328.                 os.environ[name] = unknown
329.             update_extend(extension_extra, known)
330. 
331. if is_msvc(new_compiler(compiler=self.compiler)):
332. # Assume we have to disable /OPT:REF for MSVC with ffmpeg
333.             config = {
334. 'extra_link_args': ['/OPT:NOREF'],
335.             }
336.             update_extend(extension_extra, config)
337. 
338. # Check if we're using pkg-config or not
339. if self.no_pkg_config:
340. # Simply assume we have everything we need!
341.             config = {
342. 'libraries':    ['avformat', 'avcodec', 'avdevice', 'avutil', 'avfilter',
343. 'swscale', 'swresample'],
344. 'library_dirs': [],
345. 'include_dirs': []
346.             }
347.             update_extend(extension_extra, config)
348. for ext in self.distribution.ext_modules:
349. for key, value in extension_extra.items():
350. setattr(ext, key, value)
351. return
352. 
353. # We're using pkg-config:
354.         errors = []
355. 
356. # Get the config for the libraries that we require.
357. for name in 'libavformat', 'libavcodec', 'libavdevice', 'libavutil', 'libavfilter', 'libswscale', 'libswresample':
358.             config = get_library_config(name)
359. if config:
360.                 update_extend(extension_extra, config)
361. # We don't need macros for these, since they all must exist.
362. else:
363.                 errors.append('Could not find ' + name + ' with pkg-config.')
364. 
365. # Don't continue if we have errors.
366. # TODO: Warn Ubuntu 12 users that they can't satisfy requirements with the
367. # default package sources.
368. if errors:
369. print('\n'.join(errors))
370.             exit(1)
371. 
372. # Normalize the extras.
373.         extension_extra.update(
374. dict((k, sorted(set(v))) for k, v in extension_extra.items())
375.         )
376. 
377. # Apply them.
378. for ext in self.distribution.ext_modules:
379. for key, value in extension_extra.items():
380. setattr(ext, key, value)
381. 
382. 
383. class CleanCommand(clean):
384. 
385.     user_options = clean.user_options + [
386.         ('sources', None,
387. "remove Cython build output (C sources)")]
388. 
389.     boolean_options = clean.boolean_options + ['sources']
390. 
391. def initialize_options(self):
392.         clean.initialize_options(self)
393.         self.sources = None
394. 
395. def run(self):
396.         clean.run(self)
397. if self.sources:
398. if os.path.exists('src'):
399.                 remove_tree('src', dry_run=self.dry_run)
400. else:
401.                 log.info("'%s' does not exist -- can't clean it", 'src')
402. 
403. 
404. class CythonizeCommand(Command):
405. 
406.     user_options = []
407. def initialize_options(self):
408. pass
409. def finalize_options(self):
410. pass
411. 
412. def run(self):
413. 
414. # Cythonize, if required. We do it individually since we must update
415. # the existing extension instead of replacing them all.
416. for i, ext in enumerate(self.distribution.ext_modules):
417. if any(s.endswith('.pyx') for s in ext.sources):
418. if is_msvc():
419.                     ext.define_macros.append(('inline', '__inline'))
420.                 new_ext = cythonize(
421.                     ext,
422.                     compiler_directives=dict(
423.                         c_string_type='str',
424.                         c_string_encoding='ascii',
425.                         embedsignature=True,
426.                         language_level=2,
427.                     ),
428.                     build_dir='src',
429.                     include_path=ext.include_dirs,
430.                 )[0]
431.                 ext.sources = new_ext.sources
432. 
433. 
434. class BuildExtCommand(build_ext):
435. 
436. if os.name != 'nt':
437.         user_options = build_ext.user_options + [
438.             ('no-pkg-config', None,
439. "do not use pkg-config to configure dependencies")]
440. 
441.         boolean_options = build_ext.boolean_options + ['no-pkg-config']
442. 
443. def initialize_options(self):
444.             build_ext.initialize_options(self)
445.             self.no_pkg_config = None
446. else:
447.         no_pkg_config = 1
448. 
449. def run(self):
450. 
451. # Propagate build options to config
452.         obj = self.distribution.get_command_obj('config')
453.         obj.compiler = self.compiler
454.         obj.no_pkg_config = self.no_pkg_config
455.         obj.include_dirs = self.include_dirs
456.         obj.libraries = self.libraries
457.         obj.library_dirs = self.library_dirs
458. 
459.         self.run_command('config')
460. 
461. # We write a header file containing everything we have discovered by
462. # inspecting the libraries which exist. This is the main mechanism we
463. # use to detect differenced between FFmpeg and Libav.
464. 
465.         include_dir = os.path.join(self.build_temp, 'include')
466.         pyav_dir = os.path.join(include_dir, 'pyav')
467. try:
468.             os.makedirs(pyav_dir)
469. except OSError as e:
470. if e.errno != errno.EEXIST:
471. raise
472.         header_path = os.path.join(pyav_dir, 'config.h')
473. print('writing', header_path)
474. with open(header_path, 'w') as fh:
475.             fh.write('#ifndef PYAV_COMPAT_H\n')
476.             fh.write('#define PYAV_COMPAT_H\n')
477. for k, v in sorted(config_macros.items()):
478.                 fh.write('#define %s %s\n' % (k, v))
479.             fh.write('#endif\n')
480. 
481.         self.include_dirs = self.include_dirs or []
482.         self.include_dirs.append(include_dir)
483. # Propagate config to cythonize.
484. for i, ext in enumerate(self.distribution.ext_modules):
485.             unique_extend(ext.include_dirs, self.include_dirs)
486.             unique_extend(ext.library_dirs, self.library_dirs)
487.             unique_extend(ext.libraries, self.libraries)
488. 
489.         self.run_command('cythonize')
490.         build_ext.run(self)
491. 
492. 
493. setup(
494. 
495.     name='av',
496.     version=version,
497.     description="Pythonic bindings for FFmpeg's libraries.",
498. 
499.     author="Mike Boers",
500.     author_email="pyav@mikeboers.com",
501. 
502.     url="https://github.com/mikeboers/PyAV",
503. 
504.     packages=find_packages(exclude=['build*', 'examples*', 'scratchpad*', 'tests*']),
505. 
506.     zip_safe=False,
507.     ext_modules=ext_modules,
508. 
509.     cmdclass={
510. 'build_ext': BuildExtCommand,
511. 'clean': CleanCommand,
512. 'config': ConfigCommand,
513. 'cythonize': CythonizeCommand,
514.     },
515. 
516.     test_suite='tests',
517. 
518.     entry_points={
519. 'console_scripts': [
520. 'pyav = av.__main__:main',
521.         ],
522.     },
523. 
524.     classifiers=[
525. 'Development Status :: 5 - Production/Stable',
526. 'Intended Audience :: Developers',
527. 'License :: OSI Approved :: BSD License',
528. 'Natural Language :: English',
529. 'Operating System :: MacOS :: MacOS X',
530. 'Operating System :: POSIX',
531. 'Operating System :: Unix',
532. 'Operating System :: Microsoft :: Windows',
533. 'Programming Language :: Cython',
534. 'Programming Language :: Python :: 2.7',
535. 'Programming Language :: Python :: 3.4',
536. 'Programming Language :: Python :: 3.5',
537. 'Programming Language :: Python :: 3.6',
538. 'Programming Language :: Python :: 3.7',
539. 'Topic :: Software Development :: Libraries :: Python Modules',
540. 'Topic :: Multimedia :: Sound/Audio',
541. 'Topic :: Multimedia :: Sound/Audio :: Conversion',
542. 'Topic :: Multimedia :: Video',
543. 'Topic :: Multimedia :: Video :: Conversion',
544.    ],
545. 
546.     distclass=distclass,
547. 
548. )


相关文章
|
1月前
|
自然语言处理 数据处理 Python
python操作和解析ppt文件 | python小知识
本文将带你从零开始,了解PPT解析的工具、工作原理以及常用的基本操作,并提供具体的代码示例和必要的说明【10月更文挑战第4天】
304 60
|
29天前
|
安全 Linux 数据安全/隐私保护
python知识点100篇系列(15)-加密python源代码为pyd文件
【10月更文挑战第5天】为了保护Python源码不被查看,可将其编译成二进制文件(Windows下为.pyd,Linux下为.so)。以Python3.8为例,通过Cython工具,先写好Python代码并加入`# cython: language_level=3`指令,安装easycython库后,使用`easycython *.py`命令编译源文件,最终生成.pyd文件供直接导入使用。
python知识点100篇系列(15)-加密python源代码为pyd文件
|
11天前
|
开发者 Python
Python中__init__.py文件的作用
`__init__.py`文件在Python包管理中扮演着重要角色,通过标识目录为包、初始化包、控制导入行为、支持递归包结构以及定义包的命名空间,`__init__.py`文件为组织和管理Python代码提供了强大支持。理解并正确使用 `__init__.py`文件,可以帮助开发者更好地组织代码,提高代码的可维护性和可读性。
15 2
|
1月前
|
Linux 区块链 Python
Python实用记录(十三):python脚本打包exe文件并运行
这篇文章介绍了如何使用PyInstaller将Python脚本打包成可执行文件(exe),并提供了详细的步骤和注意事项。
51 1
Python实用记录(十三):python脚本打包exe文件并运行
|
27天前
|
Java Python
> python知识点100篇系列(19)-使用python下载文件的几种方式
【10月更文挑战第7天】本文介绍了使用Python下载文件的五种方法,包括使用requests、wget、线程池、urllib3和asyncio模块。每种方法适用于不同的场景,如单文件下载、多文件并发下载等,提供了丰富的选择。
|
28天前
|
数据安全/隐私保护 流计算 开发者
python知识点100篇系列(18)-解析m3u8文件的下载视频
【10月更文挑战第6天】m3u8是苹果公司推出的一种视频播放标准,采用UTF-8编码,主要用于记录视频的网络地址。HLS(Http Live Streaming)是苹果公司提出的一种基于HTTP的流媒体传输协议,通过m3u8索引文件按序访问ts文件,实现音视频播放。本文介绍了如何通过浏览器找到m3u8文件,解析m3u8文件获取ts文件地址,下载ts文件并解密(如有必要),最后使用ffmpeg合并ts文件为mp4文件。
|
1月前
|
JSON 数据格式 Python
Python实用记录(十四):python统计某个单词在TXT/JSON文件中出现的次数
这篇文章介绍了一个Python脚本,用于统计TXT或JSON文件中特定单词的出现次数。它包含两个函数,分别处理文本和JSON文件,并通过命令行参数接收文件路径、目标单词和文件格式。文章还提供了代码逻辑的解释和示例用法。
41 0
Python实用记录(十四):python统计某个单词在TXT/JSON文件中出现的次数
|
1月前
|
计算机视觉 Python
Python操作PDF文件
Python操作PDF文件
|
1月前
|
Python
Python实用记录(十二):文件夹下所有文件重命名以及根据图片路径保存到新路径下保存
这篇文章介绍了如何使用Python脚本对TTK100_VOC数据集中的JPEGImages文件夹下的图片文件进行批量重命名,并将它们保存到指定的新路径。
32 0
|
1月前
|
Python
如何利用Python快捷地操作文件和文件夹
关注B站用户“肆十二-”,观看更多实战教学视频。本文介绍Python的shutil库,涵盖文件和文件夹的复制、移动、删除及归档等高级操作,提供实用代码示例。
24 0