style: format python files with isort and double-quote-string-fixer

This commit is contained in:
Fu Hanxi
2021-01-26 10:49:01 +08:00
parent dc8402ea61
commit 0146f258d7
276 changed files with 8241 additions and 8162 deletions
+23 -23
View File
@@ -6,11 +6,11 @@
#
# Then emits the new 'idf-info' event which has information read from IDF
# build system, that other extensions can use to generate relevant data.
import json
import os.path
import shutil
import sys
import subprocess
import json
import sys
# this directory also contains the dummy IDF project
project_path = os.path.abspath(os.path.dirname(__file__))
@@ -23,7 +23,7 @@ def setup(app):
# Setup some common paths
try:
build_dir = os.environ["BUILDDIR"] # TODO see if we can remove this
build_dir = os.environ['BUILDDIR'] # TODO see if we can remove this
except KeyError:
build_dir = os.path.dirname(app.doctreedir.rstrip(os.sep))
@@ -43,7 +43,7 @@ def setup(app):
except KeyError:
idf_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
app.add_config_value('docs_root', os.path.join(idf_path, "docs"), 'env')
app.add_config_value('docs_root', os.path.join(idf_path, 'docs'), 'env')
app.add_config_value('idf_path', idf_path, 'env')
app.add_config_value('build_dir', build_dir, 'env') # not actually an IDF thing
app.add_event('idf-info')
@@ -55,43 +55,43 @@ def setup(app):
def generate_idf_info(app, config):
print("Running CMake on dummy project to get build info...")
print('Running CMake on dummy project to get build info...')
build_dir = os.path.dirname(app.doctreedir.rstrip(os.sep))
cmake_build_dir = os.path.join(build_dir, "build_dummy_project")
idf_py_path = os.path.join(app.config.idf_path, "tools", "idf.py")
print("Running idf.py...")
cmake_build_dir = os.path.join(build_dir, 'build_dummy_project')
idf_py_path = os.path.join(app.config.idf_path, 'tools', 'idf.py')
print('Running idf.py...')
idf_py = [sys.executable,
idf_py_path,
"-B",
'-B',
cmake_build_dir,
"-C",
'-C',
project_path,
"-D",
"SDKCONFIG={}".format(os.path.join(build_dir, "dummy_project_sdkconfig"))
'-D',
'SDKCONFIG={}'.format(os.path.join(build_dir, 'dummy_project_sdkconfig'))
]
# force a clean idf.py build w/ new sdkconfig each time
# (not much slower than 'reconfigure', avoids any potential config & build versioning problems
shutil.rmtree(cmake_build_dir, ignore_errors=True)
print("Starting new dummy IDF project... ")
print('Starting new dummy IDF project... ')
if (app.config.idf_target in PREVIEW_TARGETS):
subprocess.check_call(idf_py + ["--preview", "set-target", app.config.idf_target])
subprocess.check_call(idf_py + ['--preview', 'set-target', app.config.idf_target])
else:
subprocess.check_call(idf_py + ["set-target", app.config.idf_target])
subprocess.check_call(idf_py + ['set-target', app.config.idf_target])
print("Running CMake on dummy project...")
subprocess.check_call(idf_py + ["reconfigure"])
print('Running CMake on dummy project...')
subprocess.check_call(idf_py + ['reconfigure'])
with open(os.path.join(cmake_build_dir, "project_description.json")) as f:
with open(os.path.join(cmake_build_dir, 'project_description.json')) as f:
project_description = json.load(f)
if project_description["target"] != app.config.idf_target:
if project_description['target'] != app.config.idf_target:
# this shouldn't really happen unless someone has been moving around directories inside _build, as
# the cmake_build_dir path should be target-specific
raise RuntimeError(("Error configuring the dummy IDF project for {}. " +
"Target in project description is {}. " +
"Is build directory contents corrupt?")
.format(app.config.idf_target, project_description["target"]))
raise RuntimeError(('Error configuring the dummy IDF project for {}. ' +
'Target in project description is {}. ' +
'Is build directory contents corrupt?')
.format(app.config.idf_target, project_description['target']))
app.emit('idf-info', project_description)
return []
+1 -1
View File
@@ -1,5 +1,5 @@
# Extension to generate esp_err definition as .rst
from .util import copy_if_modified, call_with_python
from .util import call_with_python, copy_if_modified
def setup(app):
+2 -2
View File
@@ -35,8 +35,8 @@ def build_subset(app, config):
# Get all docs that will be built
docs = [filename for filename in get_matching_files(app.srcdir, compile_matchers(exclude_docs))]
if not docs:
raise ValueError("No documents to build")
print("Building a subset of the documents: {}".format(docs))
raise ValueError('No documents to build')
print('Building a subset of the documents: {}'.format(docs))
# Sphinx requires a master document, if there is a document name 'index' then we pick that
index_docs = [doc for doc in docs if 'index' in doc]
+12 -11
View File
@@ -1,9 +1,10 @@
import re
import os
import os.path
import re
from docutils import io, nodes, statemachine, utils
from docutils.utils.error_reporting import SafeString, ErrorString
from docutils.parsers.rst import directives
from docutils.utils.error_reporting import ErrorString, SafeString
from sphinx.directives.other import Include as BaseInclude
from sphinx.util import logging
@@ -73,26 +74,26 @@ class StringSubstituter:
def init_sub_strings(self, config):
self.target_name = config.idf_target
self.add_pair("{IDF_TARGET_NAME}", self.TARGET_NAMES[config.idf_target])
self.add_pair("{IDF_TARGET_PATH_NAME}", config.idf_target)
self.add_pair("{IDF_TARGET_TOOLCHAIN_NAME}", self.TOOLCHAIN_NAMES[config.idf_target])
self.add_pair("{IDF_TARGET_CFG_PREFIX}", self.CONFIG_PREFIX[config.idf_target])
self.add_pair("{IDF_TARGET_TRM_EN_URL}", self.TRM_EN_URL[config.idf_target])
self.add_pair("{IDF_TARGET_TRM_CN_URL}", self.TRM_CN_URL[config.idf_target])
self.add_pair('{IDF_TARGET_NAME}', self.TARGET_NAMES[config.idf_target])
self.add_pair('{IDF_TARGET_PATH_NAME}', config.idf_target)
self.add_pair('{IDF_TARGET_TOOLCHAIN_NAME}', self.TOOLCHAIN_NAMES[config.idf_target])
self.add_pair('{IDF_TARGET_CFG_PREFIX}', self.CONFIG_PREFIX[config.idf_target])
self.add_pair('{IDF_TARGET_TRM_EN_URL}', self.TRM_EN_URL[config.idf_target])
self.add_pair('{IDF_TARGET_TRM_CN_URL}', self.TRM_CN_URL[config.idf_target])
def add_local_subs(self, matches):
for sub_def in matches:
if len(sub_def) != 2:
raise ValueError("IDF_TARGET_X substitution define invalid, val={}".format(sub_def))
raise ValueError('IDF_TARGET_X substitution define invalid, val={}'.format(sub_def))
tag = "{" + "IDF_TARGET_{}".format(sub_def[0]) + "}"
tag = '{' + 'IDF_TARGET_{}'.format(sub_def[0]) + '}'
match_default = re.match(r'^\s*default(\s*)=(\s*)\"(.*?)\"', sub_def[1])
if match_default is None:
# There should always be a default value
raise ValueError("No default value in IDF_TARGET_X substitution define, val={}".format(sub_def))
raise ValueError('No default value in IDF_TARGET_X substitution define, val={}'.format(sub_def))
match_target = re.match(r'^.*{}(\s*)=(\s*)\"(.*?)\"'.format(self.target_name), sub_def[1])
+18 -18
View File
@@ -8,35 +8,35 @@
import glob
import os
import pprint
import subprocess
import re
import subprocess
def generate_defines(app, project_description):
sdk_config_path = os.path.join(project_description["build_dir"], "config")
sdk_config_path = os.path.join(project_description['build_dir'], 'config')
# Parse kconfig macros to pass into doxygen
#
# TODO: this should use the set of "config which can't be changed" eventually,
# not the header
defines = get_defines(os.path.join(project_description["build_dir"],
"config", "sdkconfig.h"), sdk_config_path)
defines = get_defines(os.path.join(project_description['build_dir'],
'config', 'sdkconfig.h'), sdk_config_path)
# Add all SOC _caps.h headers and kconfig macros to the defines
#
# kind of a hack, be nicer to add a component info dict in project_description.json
soc_path = [p for p in project_description["build_component_paths"] if p.endswith("/soc")][0]
soc_headers = glob.glob(os.path.join(soc_path, project_description["target"],
"include", "soc", "*_caps.h"))
soc_path = [p for p in project_description['build_component_paths'] if p.endswith('/soc')][0]
soc_headers = glob.glob(os.path.join(soc_path, project_description['target'],
'include', 'soc', '*_caps.h'))
assert len(soc_headers) > 0
for soc_header in soc_headers:
defines.update(get_defines(soc_header, sdk_config_path))
# write a list of definitions to make debugging easier
with open(os.path.join(app.config.build_dir, "macro-definitions.txt"), "w") as f:
with open(os.path.join(app.config.build_dir, 'macro-definitions.txt'), 'w') as f:
pprint.pprint(defines, f)
print("Saved macro list to %s" % f.name)
print('Saved macro list to %s' % f.name)
add_tags(app, defines)
@@ -48,19 +48,19 @@ def get_defines(header_path, sdk_config_path):
# Note: we run C preprocessor here without any -I arguments (except "sdkconfig.h"), so assumption is
# that these headers are all self-contained and don't include any other headers
# not in the same directory
print("Reading macros from %s..." % (header_path))
processed_output = subprocess.check_output(["xtensa-esp32-elf-gcc", "-I", sdk_config_path,
"-dM", "-E", header_path]).decode()
for line in processed_output.split("\n"):
print('Reading macros from %s...' % (header_path))
processed_output = subprocess.check_output(['xtensa-esp32-elf-gcc', '-I', sdk_config_path,
'-dM', '-E', header_path]).decode()
for line in processed_output.split('\n'):
line = line.strip()
m = re.search("#define ([^ ]+) ?(.*)", line)
m = re.search('#define ([^ ]+) ?(.*)', line)
if m:
name = m.group(1)
value = m.group(2)
if name.startswith("_"):
if name.startswith('_'):
continue # toolchain macro
if (" " in value) or ("=" in value):
value = "" # macros that expand to multiple tokens (ie function macros) cause doxygen errors, so just mark as 'defined'
if (' ' in value) or ('=' in value):
value = '' # macros that expand to multiple tokens (ie function macros) cause doxygen errors, so just mark as 'defined'
defines[name] = value
return defines
@@ -70,7 +70,7 @@ def add_tags(app, defines):
# try to parse define values as ints and add to tags
for name, value in defines.items():
try:
define_value = int(value.strip("()"))
define_value = int(value.strip('()'))
if define_value > 0:
app.tags.add(name)
except ValueError:
+6 -4
View File
@@ -1,7 +1,9 @@
# Generate toolchain download links from toolchain info makefile
from __future__ import print_function
import os.path
from .util import copy_if_modified, call_with_python
from .util import call_with_python, copy_if_modified
def setup(app):
@@ -12,9 +14,9 @@ def setup(app):
def generate_idf_tools_links(app, project_description):
print("Generating IDF Tools list")
os.environ["IDF_MAINTAINER"] = "1"
print('Generating IDF Tools list')
os.environ['IDF_MAINTAINER'] = '1'
tools_rst = os.path.join(app.config.build_dir, 'inc', 'idf-tools-inc.rst')
tools_rst_tmp = os.path.join(app.config.build_dir, 'idf-tools-inc.rst')
call_with_python("{}/tools/idf_tools.py gen-doc --output {}".format(app.config.idf_path, tools_rst_tmp))
call_with_python('{}/tools/idf_tools.py gen-doc --output {}'.format(app.config.idf_path, tools_rst_tmp))
copy_if_modified(tools_rst_tmp, tools_rst)
+20 -18
View File
@@ -1,17 +1,19 @@
# Generate toolchain download links from toolchain info makefile
from __future__ import print_function
import os.path
from collections import namedtuple
from .util import copy_if_modified
BASE_URL = 'https://dl.espressif.com/dl/'
PlatformInfo = namedtuple("PlatformInfo", [
"platform_name",
"platform_archive_suffix",
"extension",
"unpack_cmd",
"unpack_code"
PlatformInfo = namedtuple('PlatformInfo', [
'platform_name',
'platform_archive_suffix',
'extension',
'unpack_cmd',
'unpack_code'
])
@@ -23,9 +25,9 @@ def setup(app):
def generate_toolchain_download_links(app, project_description):
print("Generating toolchain download links")
print('Generating toolchain download links')
toolchain_tmpdir = '{}/toolchain_inc'.format(app.config.build_dir)
toolchain_versions = os.path.join(app.config.idf_path, "tools/toolchain_versions.mk")
toolchain_versions = os.path.join(app.config.idf_path, 'tools/toolchain_versions.mk')
gen_toolchain_links(toolchain_versions, toolchain_tmpdir)
copy_if_modified(toolchain_tmpdir, '{}/inc'.format(app.config.build_dir))
@@ -34,11 +36,11 @@ def gen_toolchain_links(versions_file, out_dir):
version_vars = {}
with open(versions_file) as f:
for line in f:
name, var = line.partition("=")[::2]
name, var = line.partition('=')[::2]
version_vars[name.strip()] = var.strip()
gcc_version = version_vars["CURRENT_TOOLCHAIN_GCC_VERSION"]
toolchain_desc = version_vars["CURRENT_TOOLCHAIN_COMMIT_DESC_SHORT"]
gcc_version = version_vars['CURRENT_TOOLCHAIN_GCC_VERSION']
toolchain_desc = version_vars['CURRENT_TOOLCHAIN_COMMIT_DESC_SHORT']
unpack_code_linux_macos = """
::
@@ -59,10 +61,10 @@ def gen_toolchain_links(versions_file, out_dir):
"""
platform_info = [
PlatformInfo("linux64", "linux-amd64", "tar.gz", "z", unpack_code_linux_macos),
PlatformInfo("linux32", "linux-i686","tar.gz", "z", unpack_code_linux_macos),
PlatformInfo("osx", "macos", "tar.gz", "z", unpack_code_linux_macos),
PlatformInfo("win32", "win32", "zip", None, None)
PlatformInfo('linux64', 'linux-amd64', 'tar.gz', 'z', unpack_code_linux_macos),
PlatformInfo('linux32', 'linux-i686','tar.gz', 'z', unpack_code_linux_macos),
PlatformInfo('osx', 'macos', 'tar.gz', 'z', unpack_code_linux_macos),
PlatformInfo('win32', 'win32', 'zip', None, None)
]
try:
@@ -70,7 +72,7 @@ def gen_toolchain_links(versions_file, out_dir):
except OSError:
pass
with open(os.path.join(out_dir, 'download-links.inc'), "w") as links_file:
with open(os.path.join(out_dir, 'download-links.inc'), 'w') as links_file:
for p in platform_info:
archive_name = 'xtensa-esp32-elf-gcc{}-{}-{}.{}'.format(
gcc_version.replace('.', '_'), toolchain_desc, p.platform_archive_suffix, p.extension)
@@ -79,8 +81,8 @@ def gen_toolchain_links(versions_file, out_dir):
p.platform_name, BASE_URL, archive_name), file=links_file)
if p.unpack_code is not None:
with open(os.path.join(out_dir, 'unpack-code-%s.inc' % p.platform_name), "w") as f:
with open(os.path.join(out_dir, 'unpack-code-%s.inc' % p.platform_name), 'w') as f:
print(p.unpack_code.format(p.unpack_cmd, archive_name), file=f)
with open(os.path.join(out_dir, 'scratch-build-code.inc'), "w") as code_file:
with open(os.path.join(out_dir, 'scratch-build-code.inc'), 'w') as code_file:
print(scratch_build_code_linux_macos.format(toolchain_desc), file=code_file)
@@ -4,17 +4,18 @@
# Sphinx extension to generate ReSTructured Text .inc snippets
# with version-based content for this IDF version
from __future__ import print_function
from __future__ import unicode_literals
from io import open
from .util import copy_if_modified
import subprocess
from __future__ import print_function, unicode_literals
import os
import re
import subprocess
from io import open
from .util import copy_if_modified
TEMPLATES = {
"en": {
"git-clone-bash": """
'en': {
'git-clone-bash': """
.. code-block:: bash
mkdir -p ~/esp
@@ -22,7 +23,7 @@ TEMPLATES = {
git clone %(clone_args)s--recursive https://github.com/espressif/esp-idf.git
""",
"git-clone-windows": """
'git-clone-windows': """
.. code-block:: batch
mkdir %%userprofile%%\\esp
@@ -30,8 +31,8 @@ TEMPLATES = {
git clone %(clone_args)s--recursive https://github.com/espressif/esp-idf.git
""",
"git-clone-notes": {
"template": """
'git-clone-notes': {
'template': """
.. note::
%(extra_note)s
@@ -40,35 +41,35 @@ TEMPLATES = {
%(zipfile_note)s
""",
"master": 'This command will clone the master branch, which has the latest development ("bleeding edge") '
'master': 'This command will clone the master branch, which has the latest development ("bleeding edge") '
'version of ESP-IDF. It is fully functional and updated on weekly basis with the most recent features and bugfixes.',
"branch": 'The ``git clone`` option ``-b %(clone_arg)s`` tells git to clone the %(ver_type)s in the ESP-IDF repository ``git clone`` '
'branch': 'The ``git clone`` option ``-b %(clone_arg)s`` tells git to clone the %(ver_type)s in the ESP-IDF repository ``git clone`` '
'corresponding to this version of the documentation.',
"zipfile": {
"stable": 'As a fallback, it is also possible to download a zip file of this stable release from the `Releases page`_. '
'zipfile': {
'stable': 'As a fallback, it is also possible to download a zip file of this stable release from the `Releases page`_. '
'Do not download the "Source code" zip file(s) generated automatically by GitHub, they do not work with ESP-IDF.',
"unstable": 'GitHub\'s "Download zip file" feature does not work with ESP-IDF, a ``git clone`` is required. As a fallback, '
'unstable': 'GitHub\'s "Download zip file" feature does not work with ESP-IDF, a ``git clone`` is required. As a fallback, '
'`Stable version`_ can be installed without Git.'
}, # zipfile
}, # git-clone-notes
"version-note": {
"master": """
'version-note': {
'master': """
.. note::
This is documentation for the master branch (latest version) of ESP-IDF. This version is under continual development.
`Stable version`_ documentation is available, as well as other :doc:`/versions`.
""",
"stable": """
'stable': """
.. note::
This is documentation for stable version %s of ESP-IDF. Other :doc:`/versions` are also available.
""",
"branch": """
'branch': """
.. note::
This is documentation for %s ``%s`` of ESP-IDF. Other :doc:`/versions` are also available.
"""
}, # version-note
}, # en
"zh_CN": {
"git-clone-bash": """
'zh_CN': {
'git-clone-bash': """
.. code-block:: bash
mkdir -p ~/esp
@@ -76,7 +77,7 @@ TEMPLATES = {
git clone %(clone_args)s--recursive https://github.com/espressif/esp-idf.git
""",
"git-clone-windows": """
'git-clone-windows': """
.. code-block:: batch
mkdir %%userprofile%%\\esp
@@ -84,8 +85,8 @@ TEMPLATES = {
git clone %(clone_args)s--recursive https://github.com/espressif/esp-idf.git
""",
"git-clone-notes": {
"template": """
'git-clone-notes': {
'template': """
.. note::
%(extra_note)s
@@ -94,24 +95,24 @@ TEMPLATES = {
%(zipfile_note)s
""",
"master": '此命令将克隆 master 分支,该分支保存着 ESP-IDF 的最新版本,它功能齐全,每周都会更新一些新功能并修正一些错误。',
"branch": '``git clone`` 命令的 ``-b %(clone_arg)s`` 选项告诉 git 从 ESP-IDF 仓库中克隆与此版本的文档对应的分支。',
"zipfile": {
"stable": '作为备份,还可以从 `Releases page`_ 下载此稳定版本的 zip 文件。不要下载由 GitHub 自动生成的"源代码"的 zip 文件,它们不适用于 ESP-IDF。',
"unstable": 'GitHub 中"下载 zip 文档"的功能不适用于 ESP-IDF,所以需要使用 ``git clone`` 命令。作为备份,可以在没有安装 Git 的环境中下载 '
'master': '此命令将克隆 master 分支,该分支保存着 ESP-IDF 的最新版本,它功能齐全,每周都会更新一些新功能并修正一些错误。',
'branch': '``git clone`` 命令的 ``-b %(clone_arg)s`` 选项告诉 git 从 ESP-IDF 仓库中克隆与此版本的文档对应的分支。',
'zipfile': {
'stable': '作为备份,还可以从 `Releases page`_ 下载此稳定版本的 zip 文件。不要下载由 GitHub 自动生成的"源代码"的 zip 文件,它们不适用于 ESP-IDF。',
'unstable': 'GitHub 中"下载 zip 文档"的功能不适用于 ESP-IDF,所以需要使用 ``git clone`` 命令。作为备份,可以在没有安装 Git 的环境中下载 '
'`Stable version`_ 的 zip 归档文件。'
}, # zipfile
}, # git-clone
"version-note": {
"master": """
'version-note': {
'master': """
.. note::
这是ESP-IDF master 分支(最新版本)的文档,该版本在持续开发中。还有 `Stable version`_ 的文档,以及其他版本的文档 :doc:`/versions` 供参考。
""",
"stable": """
'stable': """
.. note::
这是ESP-IDF 稳定版本 %s 的文档,还有其他版本的文档 :doc:`/versions` 供参考。
""",
"branch": """
'branch': """
.. note::
这是ESP-IDF %s ``%s`` 版本的文档,还有其他版本的文档 :doc:`/versions` 供参考。
"""
@@ -128,9 +129,9 @@ def setup(app):
def generate_version_specific_includes(app, project_description):
language = app.config.language
tmp_out_dir = os.path.join(app.config.build_dir, "version_inc")
tmp_out_dir = os.path.join(app.config.build_dir, 'version_inc')
if not os.path.exists(tmp_out_dir):
print("Creating directory %s" % tmp_out_dir)
print('Creating directory %s' % tmp_out_dir)
os.mkdir(tmp_out_dir)
template = TEMPLATES[language]
@@ -138,56 +139,56 @@ def generate_version_specific_includes(app, project_description):
version, ver_type, is_stable = get_version()
write_git_clone_inc_files(template, tmp_out_dir, version, ver_type, is_stable)
write_version_note(template["version-note"], tmp_out_dir, version, ver_type, is_stable)
copy_if_modified(tmp_out_dir, os.path.join(app.config.build_dir, "inc"))
print("Done")
write_version_note(template['version-note'], tmp_out_dir, version, ver_type, is_stable)
copy_if_modified(tmp_out_dir, os.path.join(app.config.build_dir, 'inc'))
print('Done')
def write_git_clone_inc_files(templates, out_dir, version, ver_type, is_stable):
def out_file(basename):
p = os.path.join(out_dir, "%s.inc" % basename)
print("Writing %s..." % p)
p = os.path.join(out_dir, '%s.inc' % basename)
print('Writing %s...' % p)
return p
if version == "master":
clone_args = ""
if version == 'master':
clone_args = ''
else:
clone_args = "-b %s " % version
clone_args = '-b %s ' % version
with open(out_file("git-clone-bash"), "w", encoding="utf-8") as f:
f.write(templates["git-clone-bash"] % locals())
with open(out_file('git-clone-bash'), 'w', encoding='utf-8') as f:
f.write(templates['git-clone-bash'] % locals())
with open(out_file("git-clone-windows"), "w", encoding="utf-8") as f:
f.write(templates["git-clone-windows"] % locals())
with open(out_file('git-clone-windows'), 'w', encoding='utf-8') as f:
f.write(templates['git-clone-windows'] % locals())
with open(out_file("git-clone-notes"), "w", encoding="utf-8") as f:
template = templates["git-clone-notes"]
with open(out_file('git-clone-notes'), 'w', encoding='utf-8') as f:
template = templates['git-clone-notes']
zipfile = template["zipfile"]
zipfile = template['zipfile']
if version == "master":
extra_note = template["master"]
zipfile_note = zipfile["unstable"]
if version == 'master':
extra_note = template['master']
zipfile_note = zipfile['unstable']
else:
extra_note = template["branch"] % {"clone_arg": version, "ver_type": ver_type}
zipfile_note = zipfile["stable"] if is_stable else zipfile["unstable"]
extra_note = template['branch'] % {'clone_arg': version, 'ver_type': ver_type}
zipfile_note = zipfile['stable'] if is_stable else zipfile['unstable']
f.write(template["template"] % locals())
f.write(template['template'] % locals())
print("Wrote git-clone-xxx.inc files")
print('Wrote git-clone-xxx.inc files')
def write_version_note(template, out_dir, version, ver_type, is_stable):
if version == "master":
content = template["master"]
elif ver_type == "tag" and is_stable:
content = template["stable"] % version
if version == 'master':
content = template['master']
elif ver_type == 'tag' and is_stable:
content = template['stable'] % version
else:
content = template["branch"] % (ver_type, version)
out_file = os.path.join(out_dir, "version-note.inc")
with open(out_file, "w", encoding='utf-8') as f:
content = template['branch'] % (ver_type, version)
out_file = os.path.join(out_dir, 'version-note.inc')
with open(out_file, 'w', encoding='utf-8') as f:
f.write(content)
print("%s written" % out_file)
print('%s written' % out_file)
def get_version():
@@ -196,22 +197,22 @@ def get_version():
"""
# Use git to look for a tag
try:
tag = subprocess.check_output(["git", "describe", "--exact-match"]).strip().decode('utf-8')
is_stable = re.match(r"v[0-9\.]+$", tag) is not None
return (tag, "tag", is_stable)
tag = subprocess.check_output(['git', 'describe', '--exact-match']).strip().decode('utf-8')
is_stable = re.match(r'v[0-9\.]+$', tag) is not None
return (tag, 'tag', is_stable)
except subprocess.CalledProcessError:
pass
# No tag, look at branch name from CI, this will give the correct branch name even if the ref for the branch we
# merge into has moved forward before the pipeline runs
branch = os.environ.get("CI_COMMIT_REF_NAME", None)
branch = os.environ.get('CI_COMMIT_REF_NAME', None)
if branch is not None:
return (branch, "branch", False)
return (branch, 'branch', False)
# Try to find the branch name even if docs are built locally
branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).strip().decode('utf-8')
if branch != "HEAD":
return (branch, "branch", False)
branch = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip().decode('utf-8')
if branch != 'HEAD':
return (branch, 'branch', False)
# As a last resort we return commit SHA-1, should never happen in CI/docs that should be published
return (subprocess.check_output(["git", "rev-parse", "--short", "HEAD"]).strip().decode('utf-8'), "commit", False)
return (subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).strip().decode('utf-8'), 'commit', False)
@@ -1,4 +1,5 @@
import os.path
from docutils.parsers.rst import directives
from docutils.parsers.rst.directives.misc import Include as BaseInclude
from sphinx.util.docutils import SphinxDirective
+22 -22
View File
@@ -1,7 +1,7 @@
# Extension to generate the KConfig reference list
import os.path
import sys
import subprocess
import sys
from .util import copy_if_modified
@@ -18,18 +18,18 @@ def generate_reference(app, project_description):
build_dir = os.path.dirname(app.doctreedir.rstrip(os.sep))
# Generate 'kconfig.inc' file from components' Kconfig files
print("Generating kconfig.inc from kconfig contents")
print('Generating kconfig.inc from kconfig contents')
kconfig_inc_path = '{}/inc/kconfig.inc'.format(build_dir)
temp_sdkconfig_path = '{}/sdkconfig.tmp'.format(build_dir)
kconfigs = project_description["config_environment"]["COMPONENT_KCONFIGS"].split(";")
kconfig_projbuilds = project_description["config_environment"]["COMPONENT_KCONFIGS_PROJBUILD"].split(";")
kconfigs = project_description['config_environment']['COMPONENT_KCONFIGS'].split(';')
kconfig_projbuilds = project_description['config_environment']['COMPONENT_KCONFIGS_PROJBUILD'].split(';')
sdkconfig_renames = set()
# TODO: this should be generated in project description as well, if possible
for k in kconfigs + kconfig_projbuilds:
component_dir = os.path.dirname(k)
sdkconfig_rename = os.path.join(component_dir, "sdkconfig.rename")
sdkconfig_rename = os.path.join(component_dir, 'sdkconfig.rename')
if os.path.exists(sdkconfig_rename):
sdkconfig_renames.add(sdkconfig_rename)
@@ -37,27 +37,27 @@ def generate_reference(app, project_description):
kconfig_projbuilds_source_path = '{}/inc/kconfig_projbuilds_source.in'.format(build_dir)
prepare_kconfig_files_args = [sys.executable,
"{}/tools/kconfig_new/prepare_kconfig_files.py".format(app.config.idf_path),
"--env", "COMPONENT_KCONFIGS={}".format(" ".join(kconfigs)),
"--env", "COMPONENT_KCONFIGS_PROJBUILD={}".format(" ".join(kconfig_projbuilds)),
"--env", "COMPONENT_KCONFIGS_SOURCE_FILE={}".format(kconfigs_source_path),
"--env", "COMPONENT_KCONFIGS_PROJBUILD_SOURCE_FILE={}".format(kconfig_projbuilds_source_path),
'{}/tools/kconfig_new/prepare_kconfig_files.py'.format(app.config.idf_path),
'--env', 'COMPONENT_KCONFIGS={}'.format(' '.join(kconfigs)),
'--env', 'COMPONENT_KCONFIGS_PROJBUILD={}'.format(' '.join(kconfig_projbuilds)),
'--env', 'COMPONENT_KCONFIGS_SOURCE_FILE={}'.format(kconfigs_source_path),
'--env', 'COMPONENT_KCONFIGS_PROJBUILD_SOURCE_FILE={}'.format(kconfig_projbuilds_source_path),
]
subprocess.check_call(prepare_kconfig_files_args)
confgen_args = [sys.executable,
"{}/tools/kconfig_new/confgen.py".format(app.config.idf_path),
"--kconfig", "./Kconfig",
"--sdkconfig-rename", "./sdkconfig.rename",
"--config", temp_sdkconfig_path,
"--env", "COMPONENT_KCONFIGS={}".format(" ".join(kconfigs)),
"--env", "COMPONENT_KCONFIGS_PROJBUILD={}".format(" ".join(kconfig_projbuilds)),
"--env", "COMPONENT_SDKCONFIG_RENAMES={}".format(" ".join(sdkconfig_renames)),
"--env", "COMPONENT_KCONFIGS_SOURCE_FILE={}".format(kconfigs_source_path),
"--env", "COMPONENT_KCONFIGS_PROJBUILD_SOURCE_FILE={}".format(kconfig_projbuilds_source_path),
"--env", "IDF_PATH={}".format(app.config.idf_path),
"--env", "IDF_TARGET={}".format(app.config.idf_target),
"--output", "docs", kconfig_inc_path + '.in'
'{}/tools/kconfig_new/confgen.py'.format(app.config.idf_path),
'--kconfig', './Kconfig',
'--sdkconfig-rename', './sdkconfig.rename',
'--config', temp_sdkconfig_path,
'--env', 'COMPONENT_KCONFIGS={}'.format(' '.join(kconfigs)),
'--env', 'COMPONENT_KCONFIGS_PROJBUILD={}'.format(' '.join(kconfig_projbuilds)),
'--env', 'COMPONENT_SDKCONFIG_RENAMES={}'.format(' '.join(sdkconfig_renames)),
'--env', 'COMPONENT_KCONFIGS_SOURCE_FILE={}'.format(kconfigs_source_path),
'--env', 'COMPONENT_KCONFIGS_PROJBUILD_SOURCE_FILE={}'.format(kconfig_projbuilds_source_path),
'--env', 'IDF_PATH={}'.format(app.config.idf_path),
'--env', 'IDF_TARGET={}'.format(app.config.idf_target),
'--output', 'docs', kconfig_inc_path + '.in'
]
subprocess.check_call(confgen_args, cwd=app.config.idf_path)
copy_if_modified(kconfig_inc_path + '.in', kconfig_inc_path)
+5 -4
View File
@@ -1,6 +1,7 @@
from sphinx.builders.latex import LaTeXBuilder
import os
from sphinx.builders.latex import LaTeXBuilder
# Overrides the default Sphinx latex build
class IdfLatexBuilder(LaTeXBuilder):
@@ -26,7 +27,7 @@ class IdfLatexBuilder(LaTeXBuilder):
def prepare_latex_macros(self, package_path, config):
PACKAGE_NAME = "espidf.sty"
PACKAGE_NAME = 'espidf.sty'
latex_package = ''
with open(package_path, 'r') as template:
@@ -36,7 +37,7 @@ class IdfLatexBuilder(LaTeXBuilder):
latex_package = latex_package.replace('<idf_target_title>', idf_target_title)
# Release name for the PDF front page, remove '_' as this is used for subscript in Latex
idf_release_name = "Release {}".format(config.version.replace('_', '-'))
idf_release_name = 'Release {}'.format(config.version.replace('_', '-'))
latex_package = latex_package.replace('<idf_release_name>', idf_release_name)
with open(os.path.join(self.outdir, PACKAGE_NAME), 'w') as package_file:
@@ -45,7 +46,7 @@ class IdfLatexBuilder(LaTeXBuilder):
def finish(self):
super().finish()
TEMPLATE_PATH = "../latex_templates/espidf.sty"
TEMPLATE_PATH = '../latex_templates/espidf.sty'
self.prepare_latex_macros(os.path.join(self.confdir,TEMPLATE_PATH), self.config)
+19 -18
View File
@@ -1,14 +1,15 @@
# based on http://protips.readthedocs.io/link-roles.html
from __future__ import print_function
from __future__ import unicode_literals
import re
from __future__ import print_function, unicode_literals
import os
import re
import subprocess
from docutils import nodes
from collections import namedtuple
from sphinx.transforms.post_transforms import SphinxPostTransform
from docutils import nodes
from get_github_rev import get_github_rev
from sphinx.transforms.post_transforms import SphinxPostTransform
# Creates a dict of all submodules with the format {submodule_path : (url relative to git root), commit)}
@@ -27,7 +28,7 @@ def get_submodules():
rev = sub_info[0].lstrip('-')[0:7]
path = sub_info[1].lstrip('./')
config_key_arg = "submodule.{}.url".format(path)
config_key_arg = 'submodule.{}.url'.format(path)
rel_url = subprocess.check_output(['git', 'config', '--file', gitmodules_file, '--get', config_key_arg]).decode('utf-8').lstrip('./').rstrip('\n')
submodule_dict[path] = Submodule(rel_url, rev)
@@ -38,8 +39,8 @@ def get_submodules():
def url_join(*url_parts):
""" Make a URL out of multiple components, assume first part is the https:// part and
anything else is a path component """
result = "/".join(url_parts)
result = re.sub(r"([^:])//+", r"\1/", result) # remove any // that isn't in the https:// part
result = '/'.join(url_parts)
result = re.sub(r'([^:])//+', r'\1/', result) # remove any // that isn't in the https:// part
return result
@@ -47,7 +48,7 @@ def github_link(link_type, idf_rev, submods, root_path, app_config):
def role(name, rawtext, text, lineno, inliner, options={}, content=[]):
msgs = []
BASE_URL = 'https://github.com/'
IDF_REPO = "espressif/esp-idf"
IDF_REPO = 'espressif/esp-idf'
def warning(msg):
system_msg = inliner.reporter.warning(msg)
@@ -90,31 +91,31 @@ def github_link(link_type, idf_rev, submods, root_path, app_config):
line_no = tuple(int(ln_group) for ln_group in line_no.groups() if ln_group) # tuple of (nnn,) or (nnn, NNN) for ranges
elif '#' in abs_path: # drop any other anchor from the line
abs_path = abs_path.split('#')[0]
warning("URL %s seems to contain an unusable anchor after the #, only line numbers are supported" % link)
warning('URL %s seems to contain an unusable anchor after the #, only line numbers are supported' % link)
is_dir = (link_type == 'tree')
if not os.path.exists(abs_path):
warning("IDF path %s does not appear to exist (absolute path %s)" % (rel_path, abs_path))
warning('IDF path %s does not appear to exist (absolute path %s)' % (rel_path, abs_path))
elif is_dir and not os.path.isdir(abs_path):
# note these "wrong type" warnings are not strictly needed as GitHub will apply a redirect,
# but the may become important in the future (plus make for cleaner links)
warning("IDF path %s is not a directory but role :%s: is for linking to a directory, try :%s_file:" % (rel_path, name, name))
warning('IDF path %s is not a directory but role :%s: is for linking to a directory, try :%s_file:' % (rel_path, name, name))
elif not is_dir and os.path.isdir(abs_path):
warning("IDF path %s is a directory but role :%s: is for linking to a file" % (rel_path, name))
warning('IDF path %s is a directory but role :%s: is for linking to a file' % (rel_path, name))
# check the line number is valid
if line_no:
if is_dir:
warning("URL %s contains a line number anchor but role :%s: is for linking to a directory" % (rel_path, name, name))
warning('URL %s contains a line number anchor but role :%s: is for linking to a directory' % (rel_path, name, name))
elif os.path.exists(abs_path) and not os.path.isdir(abs_path):
with open(abs_path, "r") as f:
with open(abs_path, 'r') as f:
lines = len(f.readlines())
if any(True for ln in line_no if ln > lines):
warning("URL %s specifies a range larger than file (file has %d lines)" % (rel_path, lines))
warning('URL %s specifies a range larger than file (file has %d lines)' % (rel_path, lines))
if tuple(sorted(line_no)) != line_no: # second line number comes before first one!
warning("URL %s specifies a backwards line number range" % rel_path)
warning('URL %s specifies a backwards line number range' % rel_path)
node = nodes.reference(rawtext, link_text, refuri=url, **options)
return [node], msgs
@@ -148,7 +149,7 @@ class TranslationLinkNodeTransform(SphinxPostTransform):
doc_path = env.doc2path(docname, None, None)
return_path = '../' * doc_path.count('/') # path back to the root from 'docname'
# then take off 3 more paths for language/release/targetname and build the new URL
url = "{}.html".format(os.path.join(return_path, '../../..', language, env.config.release,
url = '{}.html'.format(os.path.join(return_path, '../../..', language, env.config.release,
env.config.idf_target, docname))
node.replace_self(nodes.reference(rawtext, link_text, refuri=url, **options))
else:
+66 -65
View File
@@ -1,20 +1,21 @@
# Extension to generate Doxygen XML include files, with IDF config & soc macros included
from __future__ import print_function
from __future__ import unicode_literals
from io import open
from __future__ import print_function, unicode_literals
import os
import os.path
import re
import subprocess
from io import open
from .util import copy_if_modified
ALL_KINDS = [
("function", "Functions"),
("union", "Unions"),
("struct", "Structures"),
("define", "Macros"),
("typedef", "Type Definitions"),
("enum", "Enumerations")
('function', 'Functions'),
('union', 'Unions'),
('struct', 'Structures'),
('define', 'Macros'),
('typedef', 'Type Definitions'),
('enum', 'Enumerations')
]
"""list of items that will be generated for a single API file
"""
@@ -30,27 +31,27 @@ def generate_doxygen(app, defines):
build_dir = os.path.dirname(app.doctreedir.rstrip(os.sep))
# Call Doxygen to get XML files from the header files
print("Calling Doxygen to generate latest XML files")
print('Calling Doxygen to generate latest XML files')
doxy_env = os.environ
doxy_env.update({
"ENV_DOXYGEN_DEFINES": " ".join('{}={}'.format(key, value) for key, value in defines.items()),
"IDF_PATH": app.config.idf_path,
"IDF_TARGET": app.config.idf_target,
'ENV_DOXYGEN_DEFINES': ' '.join('{}={}'.format(key, value) for key, value in defines.items()),
'IDF_PATH': app.config.idf_path,
'IDF_TARGET': app.config.idf_target,
})
doxyfile_dir = os.path.join(app.config.docs_root, "doxygen")
doxyfile_main = os.path.join(doxyfile_dir, "Doxyfile_common")
doxyfile_target = os.path.join(doxyfile_dir, "Doxyfile_" + app.config.idf_target)
print("Running doxygen with doxyfiles {} and {}".format(doxyfile_main, doxyfile_target))
doxyfile_dir = os.path.join(app.config.docs_root, 'doxygen')
doxyfile_main = os.path.join(doxyfile_dir, 'Doxyfile_common')
doxyfile_target = os.path.join(doxyfile_dir, 'Doxyfile_' + app.config.idf_target)
print('Running doxygen with doxyfiles {} and {}'.format(doxyfile_main, doxyfile_target))
# It's possible to have doxygen log warnings to a file using WARN_LOGFILE directive,
# but in some cases it will still log an error to stderr and return success!
#
# So take all of stderr and redirect it to a logfile (will contain warnings and errors)
logfile = os.path.join(build_dir, "doxygen-warning-log.txt")
logfile = os.path.join(build_dir, 'doxygen-warning-log.txt')
with open(logfile, "w") as f:
with open(logfile, 'w') as f:
# note: run Doxygen in the build directory, so the xml & xml_in files end up in there
subprocess.check_call(["doxygen", doxyfile_main], env=doxy_env, cwd=build_dir, stderr=f)
subprocess.check_call(['doxygen', doxyfile_main], env=doxy_env, cwd=build_dir, stderr=f)
# Doxygen has generated XML files in 'xml' directory.
# Copy them to 'xml_in', only touching the files which have changed.
@@ -69,11 +70,11 @@ def convert_api_xml_to_inc(app, doxyfiles):
"""
build_dir = app.config.build_dir
xml_directory_path = "{}/xml".format(build_dir)
inc_directory_path = "{}/inc".format(build_dir)
xml_directory_path = '{}/xml'.format(build_dir)
inc_directory_path = '{}/inc'.format(build_dir)
if not os.path.isdir(xml_directory_path):
raise RuntimeError("Directory {} does not exist!".format(xml_directory_path))
raise RuntimeError('Directory {} does not exist!'.format(xml_directory_path))
if not os.path.exists(inc_directory_path):
os.makedirs(inc_directory_path)
@@ -83,16 +84,16 @@ def convert_api_xml_to_inc(app, doxyfiles):
print("Generating 'api_name.inc' files with Doxygen directives")
for header_file_path in header_paths:
api_name = get_api_name(header_file_path)
inc_file_path = inc_directory_path + "/" + api_name + ".inc"
inc_file_path = inc_directory_path + '/' + api_name + '.inc'
rst_output = generate_directives(header_file_path, xml_directory_path)
previous_rst_output = ''
if os.path.isfile(inc_file_path):
with open(inc_file_path, "r", encoding='utf-8') as inc_file_old:
with open(inc_file_path, 'r', encoding='utf-8') as inc_file_old:
previous_rst_output = inc_file_old.read()
if previous_rst_output != rst_output:
with open(inc_file_path, "w", encoding='utf-8') as inc_file:
with open(inc_file_path, 'w', encoding='utf-8') as inc_file:
inc_file.write(rst_output)
@@ -108,11 +109,11 @@ def get_doxyfile_input_paths(app, doxyfile_path):
print("Getting Doxyfile's INPUT")
with open(doxyfile_path, "r", encoding='utf-8') as input_file:
with open(doxyfile_path, 'r', encoding='utf-8') as input_file:
line = input_file.readline()
# read contents of Doxyfile until 'INPUT' statement
while line:
if line.find("INPUT") == 0:
if line.find('INPUT') == 0:
break
line = input_file.readline()
@@ -124,13 +125,13 @@ def get_doxyfile_input_paths(app, doxyfile_path):
# we have reached the end of 'INPUT' statement
break
# process only lines that are not comments
if line.find("#") == -1:
if line.find('#') == -1:
# extract header file path inside components folder
m = re.search("components/(.*\.h)", line) # noqa: W605 - regular expression
m = re.search('components/(.*\.h)', line) # noqa: W605 - regular expression
header_file_path = m.group(1)
# Replace env variable used for multi target header
header_file_path = header_file_path.replace("$(IDF_TARGET)", app.config.idf_target)
header_file_path = header_file_path.replace('$(IDF_TARGET)', app.config.idf_target)
doxyfile_INPUT.append(header_file_path)
@@ -150,8 +151,8 @@ def get_api_name(header_file_path):
The name of API.
"""
api_name = ""
regex = r".*/(.*)\.h"
api_name = ''
regex = r'.*/(.*)\.h'
m = re.search(regex, header_file_path)
if m:
api_name = m.group(1)
@@ -173,15 +174,15 @@ def generate_directives(header_file_path, xml_directory_path):
api_name = get_api_name(header_file_path)
# in XLT file name each "_" in the api name is expanded by Doxygen to "__"
xlt_api_name = api_name.replace("_", "__")
xml_file_path = "%s/%s_8h.xml" % (xml_directory_path, xlt_api_name)
xlt_api_name = api_name.replace('_', '__')
xml_file_path = '%s/%s_8h.xml' % (xml_directory_path, xlt_api_name)
rst_output = ""
rst_output = ''
rst_output = ".. File automatically generated by 'gen-dxd.py'\n"
rst_output += "\n"
rst_output += get_rst_header("Header File")
rst_output += "* :component_file:`" + header_file_path + "`\n"
rst_output += "\n"
rst_output += '\n'
rst_output += get_rst_header('Header File')
rst_output += '* :component_file:`' + header_file_path + '`\n'
rst_output += '\n'
try:
import xml.etree.cElementTree as ET
@@ -206,10 +207,10 @@ def get_rst_header(header_name):
"""
rst_output = ""
rst_output += header_name + "\n"
rst_output += "^" * len(header_name) + "\n"
rst_output += "\n"
rst_output = ''
rst_output += header_name + '\n'
rst_output += '^' * len(header_name) + '\n'
rst_output += '\n'
return rst_output
@@ -226,14 +227,14 @@ def select_unions(innerclass_list):
"""
rst_output = ""
rst_output = ''
for line in innerclass_list.splitlines():
# union is denoted by "union" at the beginning of line
if line.find("union") == 0:
union_id, union_name = re.split(r"\t+", line)
rst_output += ".. doxygenunion:: "
if line.find('union') == 0:
union_id, union_name = re.split(r'\t+', line)
rst_output += '.. doxygenunion:: '
rst_output += union_name
rst_output += "\n"
rst_output += '\n'
return rst_output
@@ -251,20 +252,20 @@ def select_structs(innerclass_list):
"""
rst_output = ""
rst_output = ''
for line in innerclass_list.splitlines():
# structure is denoted by "struct" at the beginning of line
if line.find("struct") == 0:
if line.find('struct') == 0:
# skip structures that are part of union
# they are documented by 'doxygenunion' directive
if line.find("::") > 0:
if line.find('::') > 0:
continue
struct_id, struct_name = re.split(r"\t+", line)
rst_output += ".. doxygenstruct:: "
struct_id, struct_name = re.split(r'\t+', line)
rst_output += '.. doxygenstruct:: '
rst_output += struct_name
rst_output += "\n"
rst_output += " :members:\n"
rst_output += "\n"
rst_output += '\n'
rst_output += ' :members:\n'
rst_output += '\n'
return rst_output
@@ -282,12 +283,12 @@ def get_directives(tree, kind):
"""
rst_output = ""
if kind in ["union", "struct"]:
innerclass_list = ""
rst_output = ''
if kind in ['union', 'struct']:
innerclass_list = ''
for elem in tree.iterfind('compounddef/innerclass'):
innerclass_list += elem.attrib["refid"] + "\t" + elem.text + "\n"
if kind == "union":
innerclass_list += elem.attrib['refid'] + '\t' + elem.text + '\n'
if kind == 'union':
rst_output += select_unions(innerclass_list)
else:
rst_output += select_structs(innerclass_list)
@@ -295,10 +296,10 @@ def get_directives(tree, kind):
for elem in tree.iterfind(
'compounddef/sectiondef/memberdef[@kind="%s"]' % kind):
name = elem.find('name')
rst_output += ".. doxygen%s:: " % kind
rst_output += name.text + "\n"
rst_output += '.. doxygen%s:: ' % kind
rst_output += name.text + '\n'
if rst_output:
all_kinds_dict = dict(ALL_KINDS)
rst_output = get_rst_header(all_kinds_dict[kind]) + rst_output + "\n"
rst_output = get_rst_header(all_kinds_dict[kind]) + rst_output + '\n'
return rst_output
+5 -4
View File
@@ -15,10 +15,11 @@
# limitations under the License.
from __future__ import unicode_literals
from io import open
import os
import shutil
import sys
from io import open
try:
import urllib.request
@@ -33,10 +34,10 @@ def files_equal(path_1, path_2):
if not os.path.exists(path_1) or not os.path.exists(path_2):
return False
file_1_contents = ''
with open(path_1, "r", encoding='utf-8') as f_1:
with open(path_1, 'r', encoding='utf-8') as f_1:
file_1_contents = f_1.read()
file_2_contents = ''
with open(path_2, "r", encoding='utf-8') as f_2:
with open(path_2, 'r', encoding='utf-8') as f_2:
file_2_contents = f_2.read()
return file_1_contents == file_2_contents
@@ -63,7 +64,7 @@ def copy_if_modified(src_path, dst_path):
def download_file_if_missing(from_url, to_path):
filename_with_path = to_path + "/" + os.path.basename(from_url)
filename_with_path = to_path + '/' + os.path.basename(from_url)
exists = os.path.isfile(filename_with_path)
if exists:
print("The file '%s' already exists" % (filename_with_path))