KApiDox

utils.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright 2014 Aurélien Gâteau <[email protected]>
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 from fnmatch import fnmatch
27 import logging
28 import os
29 import re
30 import subprocess
31 import shutil
32 import sys
33 import tempfile
34 import requests
35 
36 ## @package kapidox.utils
37 #
38 # Multiple usage utils.
39 #
40 # This module contains code which is shared between depdiagram-prepare and
41 # other components.
42 #
43 # Code in this dir should not import any module which is not shipped with
44 # Python because this module is used by depdiagram-prepare, which must be able
45 # to run on builds.kde.org, which may not have all the required dependencies.
46 #
47 
48 def setup_logging():
49  FORMAT = '%(asctime)s %(levelname)s %(message)s'
50  logging.basicConfig(format=FORMAT, datefmt='%H:%M:%S', level=logging.DEBUG)
51 
52 
53 def tolist(a):
54  """ Return a list based on `a`. """
55  return a if type(a) is list else [a]
56 
57 
58 def serialize_name(name):
59  """ Return a serialized name.
60 
61  For now it only replaces ' ' with '_' and lower the letters.
62  """
63  if name is not None:
64  return '_'.join(name.lower().split(' '))
65  else:
66  return None
67 
68 def set_repopath(id):
69  """ Return the repopath for the repo id, queried from projects.kde.org
70 
71  Args:
72  id: unique KDE repo identifier
73  """
74  if id is None:
75  return None
76 
77  try:
78  r = requests.get('https://projects.kde.org/api/v1/identifier/' + id)
79  return r.json()['repo']
80  except Exception as exc:
81  # Catch all exceptions here: whatever fails in this function should not
82  # cause the code to fail
83  logging.warning("Failed to get repository url for '{}' from projects.kde.org: {}".format(id, exc))
84  return None
85 
86 def set_maintainers(maintainer_keys, all_maintainers):
87  """ Expend the name of the maintainers.
88 
89  Args:
90  dictionary: (dict) Dictionary from which the name to expend will be read.
91  key: (string) Key of the dictionary where the name to expend is saved.
92  all_maintainers: (dict of dict) Look-up table where the names and emails of
93  the maintainers are stored.
94 
95  Examples:
96  >>> maintainer_keys = ['arthur', 'toto']
97 
98  >>> myteam = {'arthur': {'name': 'Arthur Pendragon',
99  'email': '[email protected]'},
100  'toto': {'name': 'Toto',
101  'email: '[email protected]'}
102  }
103 
104  >>> set_maintainers(maintainer_keys, my_team)
105  """
106 
107  if not maintainer_keys:
108  maintainers = []
109  elif isinstance(maintainer_keys, list):
110  maintainers = map(lambda x: all_maintainers.get(x, None),
111  maintainer_keys)
112  else:
113  maintainers = [all_maintainers.get(maintainer_keys, None)]
114 
115  maintainers = [x for x in maintainers if x is not None]
116  return maintainers
117 
118 
119 def parse_fancyname(fw_dir):
120  """Return the framework name for a given source dir
121 
122  The framework name is the name of the toplevel CMake project
123  """
124  cmakelists_path = os.path.join(fw_dir, "CMakeLists.txt")
125  if not os.path.exists(cmakelists_path):
126  logging.error("No CMakeLists.txt in {}".format(fw_dir))
127  return None
128  project_re = re.compile(r"project\s*\(\s*([\w\-\_]+)", re.I)
129  with open(cmakelists_path) as f:
130  for line in f.readlines():
131  match = project_re.search(line)
132  if match:
133  return match.group(1)
134  logging.error("Failed to find framework name: Could not find a "
135  "'project()' command in {}.".format(cmakelists_path))
136  return None
137 
138 
139 def cache_dir():
140  """Find/create a semi-long-term cache directory.
141 
142  We do not use tempdir, except as a fallback, because temporary directories
143  are intended for files that only last for the program's execution.
144  """
145  cachedir = None
146  if sys.platform == 'darwin':
147  try:
148  from AppKit import NSSearchPathForDirectoriesInDomains
149  # http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html#//apple_ref/c/func/NSSearchPathForDirectoriesInDomains
150  # NSApplicationSupportDirectory = 14
151  # NSUserDomainMask = 1
152  # True for expanding the tilde into a fully qualified path
153  cachedir = os.path.join(
154  NSSearchPathForDirectoriesInDomains(14, 1, True)[0],
155  'KApiDox')
156  except:
157  pass
158  elif os.name == "posix":
159  if 'HOME' in os.environ and os.path.exists(os.environ['HOME']):
160  cachedir = os.path.join(os.environ['HOME'], '.cache', 'kapidox')
161  elif os.name == "nt":
162  if 'APPDATA' in os.environ and os.path.exists(os.environ['APPDATA']):
163  cachedir = os.path.join(os.environ['APPDATA'], 'KApiDox')
164  if cachedir is None:
165  cachedir = os.path.join(tempfile.gettempdir(), 'kapidox')
166  if not os.path.isdir(cachedir):
167  os.makedirs(cachedir)
168  return cachedir
169 
170 
171 def svn_export(remote, local, overwrite=False):
172  """Wraps svn export.
173 
174  Args:
175  remote: (string) the remote url.
176  local: (string) the local path where to dowload.
177  overwrite: (bool) whether to overwrite `local` or not. (optional,
178  default = False)
179 
180  Returns:
181  True if success.
182 
183  Raises:
184  FileNotFoundError: &nbsp;
185  subprocess.CalledProcessError: &nbsp;
186  """
187  try:
188  import svn.core
189  import svn.client
190  logging.debug("Using Python libsvn bindings to fetch %s", remote)
191  ctx = svn.client.create_context()
192  ctx.auth_baton = svn.core.svn_auth_open([])
193 
194  latest = svn.core.svn_opt_revision_t()
195  latest.type = svn.core.svn_opt_revision_head
196 
197  svn.client.export(remote, local, latest, True, ctx)
198  except ImportError:
199  logging.debug("Using external svn client to fetch %s", remote)
200  cmd = ['svn', 'export', '--quiet']
201  if overwrite:
202  cmd.append('--force')
203  cmd += [remote, local]
204  try:
205  subprocess.check_call(cmd, stderr=subprocess.STDOUT)
206  except subprocess.CalledProcessError as e:
207  raise subprocess.StandardException(e.output)
208  except FileNotFoundError as e:
209  logging.debug("External svn client not found")
210  return False
211  # subversion will set the timestamp to match the server
212  os.utime(local, None)
213  return True
214 
215 
216 def copy_dir_contents(directory, dest):
217  """Copy the contents of a directory
218 
219  Args:
220  directory: (string) the directory to copy the contents of.
221  dest: (string) the directory to copy them into.
222  """
223  ignored = ['CMakeLists.txt']
224  ignore = shutil.ignore_patterns(*ignored)
225  for fn in os.listdir(directory):
226  f = os.path.join(directory, fn)
227  if os.path.isfile(f):
228  docopy = True
229  for i in ignored:
230  if fnmatch(fn, i):
231  docopy = False
232  break
233  if docopy:
234  shutil.copy(f, dest)
235  elif os.path.isdir(f):
236  dest_f = os.path.join(dest, fn)
237  if os.path.isdir(dest_f):
238  shutil.rmtree(dest_f)
239  shutil.copytree(f, dest_f, ignore=ignore)
240 
241 
242 _KAPIDOX_VERSION = None
243 def get_kapidox_version():
244  """Get commit id of running code if it is running from git repository.
245 
246  May return an empty string if it failed to extract the commit id.
247 
248  Assumes .git/HEAD looks like this:
249 
250  ref: refs/heads/master
251 
252  and assumes .git/refs/heads/master contains the commit id
253  """
254  global _KAPIDOX_VERSION
255 
256  if _KAPIDOX_VERSION is not None:
257  return _KAPIDOX_VERSION
258 
259  _KAPIDOX_VERSION = ""
260  bin_dir = os.path.dirname(sys.argv[0])
261  git_dir = os.path.join(bin_dir, "..", ".git")
262  if not os.path.isdir(git_dir):
263  # Looks like we are not running from the git repo, exit silently
264  return _KAPIDOX_VERSION
265 
266  git_HEAD = os.path.join(git_dir, "HEAD")
267  if not os.path.isfile(git_HEAD):
268  logging.warning("Getting git info failed: {} is not a file".format(git_HEAD))
269  return _KAPIDOX_VERSION
270 
271  try:
272  line = open(git_HEAD).readline()
273  ref_name = line.split(": ")[1].strip()
274  with open(os.path.join(git_dir, ref_name)) as f:
275  _KAPIDOX_VERSION = f.read().strip()
276  except Exception as exc:
277  # Catch all exceptions here: whatever fails in this function should not
278  # cause the code to fail
279  logging.warning("Getting git info failed: {}".format(exc))
280  return _KAPIDOX_VERSION
281 
282 
283 def find_dot_files(dot_dir):
284  """Returns a list of path to files ending with .dot in subdirs of `dot_dir`."""
285  lst = []
286  for (root, dirs, files) in os.walk(dot_dir):
287  lst.extend([os.path.join(root, x) for x in files if x.endswith('.dot')])
288  return lst
def serialize_name(name)
Return a serialized name.
Definition: utils.py:62
def parse_fancyname(fw_dir)
Return the framework name for a given source dir.
Definition: utils.py:127
def find_dot_files(dot_dir)
Returns a list of path to files ending with .dot in subdirs of dot_dir.
Definition: utils.py:297
def set_repopath(id)
Return the repopath for the repo id, queried from projects.kde.org.
Definition: utils.py:73
def tolist(a)
Return a list based on a.
Definition: utils.py:54
def cache_dir()
Find/create a semi-long-term cache directory.
Definition: utils.py:148
def svn_export(remote, local, overwrite=False)
Wraps svn export.
Definition: utils.py:190
def get_kapidox_version()
Get commit id of running code if it is running from git repository.
Definition: utils.py:260
def set_maintainers(maintainer_keys, all_maintainers)
Expend the name of the maintainers.
Definition: utils.py:109
def copy_dir_contents(directory, dest)
Copy the contents of a directory.
Definition: utils.py:226
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sun Aug 2 2020 22:52:46 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.