Introduce "Library Dependency Finder"

This commit is contained in:
Ivan Kravets
2016-08-01 00:14:22 +03:00
parent 22e67e6fdd
commit 513577958f
10 changed files with 337 additions and 185 deletions

View File

@ -33,37 +33,6 @@ What is ``.pioenvs`` directory
Please refer to :ref:`projectconf_pio_envs_dir`.
.. _faq_ldf:
How works Library Dependency Finder (LDF)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Library Dependency Finder is a part of PlatformIO Library Build System. It
operates with the C/C++ source files and looks for ``#include <...>``
directives. Also, LDF interprets C Preprocessor conditional macros
(``#if``, ``ifdef``, etc.). Library Dependency Finder starts
work from analyzing source files from :ref:`projectconf_pio_src_dir` by default.
If project or library contains own ``dependencies`` list (see
:ref:`libjson_dependencies`), the LDF will not looking for dependencies in
the source code. The specified libraries will be built automatically without
check.
There are different library storages where Library Dependency Finder looks for
dependencies. These storages/folders have priority. LDF operates in the next
order:
1. :ref:`projectconf_lib_extra_dirs`
2. :ref:`projectconf_pio_lib_dir`
3. :ref:`projectconf_pio_home_dir`/lib
Library Dependency Finder has a few controls from :ref:`projectconf`:
* :ref:`projectconf_lib_ignore`
* :ref:`projectconf_lib_deep_search`
* :ref:`projectconf_lib_extra_dirs`
* :ref:`projectconf_lib_compat_mode`
Command completion in Terminal
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -119,6 +119,7 @@ Contents
Quickstart <librarymanager/index>
User Guide <userguide/lib/index.rst>
librarymanager/ldf
librarymanager/config
librarymanager/creating

171
docs/librarymanager/ldf.rst Normal file
View File

@ -0,0 +1,171 @@
.. Copyright 2014-present Ivan Kravets <me@ikravets.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
.. _ldf:
Library Dependency Finder (LDF)
===============================
.. versionadded:: 3.0
Library Dependency Finder is a core part of PlatformIO Build System that
operates with the C/C++ source files and looks for ``#include ...``
directives.
In spite of the fact that Library Dependency Finder is written in pure Python,
it interprets (emulates) :ref:`ldf_c_cond_syntax` (``#ifdef``, ``if``, ``defined``,
``else``, and ``elif``) without calling ``gcc -E``. This approach allows
significantly reduce total compilation time.
Library Dependency Finder has controls that can be set up in :ref:`projectconf`:
.. hlist::
:columns: 3
* :ref:`projectconf_lib_extra_dirs`
* :ref:`projectconf_lib_force`
* :ref:`projectconf_lib_ignore`
* :ref:`projectconf_lib_compat_mode`
* :ref:`projectconf_lib_ldf_mode`
-----------
.. contents::
Storage
-------
There are different storages/folders where Library Dependency Finder looks for
libraries. These folders/path have priority and LDF operates in the next order:
1. :ref:`projectconf_lib_extra_dirs` - extra storages per build environment
2. :ref:`projectconf_pio_lib_dir` - own/private library storage per project
3. :ref:`projectconf_pio_piolibdeps_dir` - project dependencies storage used by
:ref:`librarymanager`
4. :ref:`projectconf_pio_home_dir`/lib - global storage per all projects.
.. _ldf_mode:
Dependency Finder Mode
----------------------
Library Dependency Finder starts work from analyzing source files of the
project (:ref:`projectconf_pio_src_dir`) and can work in the next modes:
* ``0`` - "manual mode", does not process source files of a project and
dependent libraries. Builds only the libraries that are specified in
manifests (:ref:`library_config`, ``module.json``) or in the :ref:`projectconf`.
* ``1`` - parses ALL C/C++ source code of the project and follows only by
nested includes/chain (``#include ...``) from the libraries.
* ``2`` - **default** - parses ALL C/C++ source code of the project and parses
ALL C/C++ source code of the each dependent library (recursively).
This mode can be changed using :ref:`projectconf_lib_ldf_mode` option in
:ref:`projectconf`.
A difference between ``1`` and ``2`` modes. For example, there are 2 libraries:
* Library "Foo" with files:
- ``Foo/foo.h``
- ``Foo/foo.cpp``
* Library "Bar" with files:
- ``Bar/bar.h``
- ``Bar/bar.cpp``
:Case 1:
* ``lib_ldf_mode = 1``
* ``Foo/foo.h`` depends on "Bar" library (contains ``#include <bar.h>``)
* ``#include <foo.h>`` is located in one of the project source files
Here are nested includes (``project file > foo.h > bar.h``) and ``LDF``
will find both libraries "Foo" and "Bar".
:Case 2:
* ``lib_ldf_mode = 1``
* ``Foo/foo.cpp`` depends on "Bar" library (contains ``#include <bar.h>``)
* ``#include <foo.h>`` is located in one of the project source files
In this case, ``LDF`` will not find "Bar" library because it doesn't know
about CPP file (``Foo/foo.cpp``).
:Case 3:
* ``lib_ldf_mode = 2``
* ``Foo/foo.cpp`` depends on "Bar" library (contains ``#include <bar.h>``)
* ``#include <foo.h>`` is located in one of the project source files
Firstly, ``LDF`` finds "Foo" library, then it parses all sources from "Foo"
library and finds ``Foo/foo.cpp`` that depends on ``#include <bar.h>``.
Secondly, it will parse all sources from "Bar" library and this operation
continues until all dependent libraries will not be parsed.
.. _ldf_compat_mode:
Compatibility Mode
------------------
Compatibility mode allows to control strictness of Library Dependency Finder.
If library contains one of manifest file (:ref:`library_config`,
``library.properties``, ``module.json``), then LDF check compatibility of this
library with real build environment. Available compatibility modes:
* ``0`` - does not check for compatibility (is not recommended)
* ``1`` - **default** - checks for the compatibility with
:ref:`projectconf_env_framework` from build environment
* ``2`` - checks for the compatibility with :ref:`projectconf_env_framework`
and :ref:`projectconf_env_platform` from build environment.
This mode can be changed using :ref:`projectconf_lib_compat_mode` option in
:ref:`projectconf`.
Manual dependencies
-------------------
If project or library contains own ``dependencies`` list (see
:ref:`libjson_dependencies`), the LDF will not looking for dependencies in
the source code. The specified libraries will be built automatically without
check.
.. _ldf_c_cond_syntax:
C Preprocessor conditional syntax
---------------------------------
In spite of the fact that Library Dependency Finder is written in pure Python,
it interprets (emulates) `C Preprocessor conditional syntax <https://gcc.gnu.org/onlinedocs/cpp/Conditional-Syntax.html#Conditional-Syntax>`_
(``#ifdef``, ``if``, ``defined``, ``else``, and ``elif``) without calling
``gcc -E``. For example,
``platformio.ini``
.. code-block:: ini
[env:myenv]
build_flags = -D MY_PROJECT_VERSION=13
``mylib.h``
.. code-block:: c
#ifdef PROJECT_VERSION
// include common file for the project
#include "my_common.h"
#endif
#if PROJECT_VERSION < 10
// this include will be ignored because does not satisfy condition above
#include "my_old.h"
#endif

View File

@ -14,6 +14,8 @@
Unit Testing
============
.. versionadded:: 3.0
`Unit Testing (wiki) <https://en.wikipedia.org/wiki/Unit_testing>`_
is a software testing method by which individual units of source code, sets
of one or more MCU program modules together with associated control data,

View File

@ -44,8 +44,8 @@ Options
``home_dir``
^^^^^^^^^^^^
Is used to store platform toolchains, frameworks, external libraries,
service data and etc.
Is used to store platform toolchains, frameworks, global libraries for
:ref: `ldf`, service data and etc.
A default value is User's home directory:
@ -60,18 +60,59 @@ This option can be overridden by global environment variable
``lib_dir``
^^^^^^^^^^^
This directory is used to store external libraries downloaded by
:ref:`librarymanager`.
You can put here your own/private libraries. The source code of each library
should be placed in separate directory, like
``lib/private_lib/[here are source files]``. This directory has the highest
priority for :ref:`ldf`.
A default value is ``%home_dir%/lib``.
A default value is ``lib`` that means that folder is located in the root of
project.
This option can be overridden by global environment variable
:envvar:`PLATFORMIO_LIB_DIR`.
.. note::
You can put here your own/private libraries. The source code of each
library should be placed in separate directory. For example,
``%lib_dir%/private_lib/[here are source files]``.
For example, see how can be organized ``Foo`` and ``Bar`` libraries:
.. code::
|--lib
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| |--Foo
| | |- Foo.c
| | |- Foo.h
|- platformio.ini
|--src
|- main.c
Then in ``src/main.c`` you should use:
.. code-block:: c
#include <Foo.h>
#include <Bar.h>
// rest H/C/CPP code
PlatformIO will find your libraries automatically, configure preprocessor's
include paths and build them.
.. _projectconf_pio_piolibdeps_dir:
``piolibdeps_dir``
^^^^^^^^^^^^^^^^^^
Internal storage where :ref:`librarymanager` will install project dependencies.
A default value is ``.piolibdeps`` that means that folder is located in the root of
project.
This option can be overridden by global environment variable
:envvar:`PLATFORMIO_PIOLIBDEPS_DIR`.
.. _projectconf_pio_src_dir:
@ -79,10 +120,8 @@ This option can be overridden by global environment variable
^^^^^^^^^^^
A path to project's source directory. PlatformIO uses it for :ref:`cmd_run`
command.
A default value is ``src`` which means that folder is located in the root of
project.
command. A default value is ``src`` that means that folder is located in the
root of project.
This option can be overridden by global environment variable
:envvar:`PLATFORMIO_SRC_DIR`.
@ -110,7 +149,7 @@ fast!
then PlatformIO will remove this folder automatically. It will be created on the
next build operation.
A default value is ``.pioenvs`` which means that folder is located in the root of
A default value is ``.pioenvs`` that means that folder is located in the root of
project.
This option can be overridden by global environment variable
@ -127,8 +166,7 @@ This option can be overridden by global environment variable
^^^^^^^^^^^^
Data directory to store contents and :ref:`platform_espressif_uploadfs`.
A default value is ``data`` which means that folder is located in the root of
A default value is ``data`` that means that folder is located in the root of
project.
This option can be overridden by global environment variable
@ -139,9 +177,8 @@ This option can be overridden by global environment variable
``test_dir``
^^^^^^^^^^^^
Directory for :ref:`unit_testing`.
A default value is ``test`` which means that folder is located in the root of
Directory where :ref:`unit_testing` engine will look for the tests.
A default value is ``test`` that means that folder is located in the root of
project.
This option can be overridden by global environment variable
@ -206,6 +243,9 @@ For example, ``[env:hello_world]``.
General options
~~~~~~~~~~~~~~~
.. contents::
:local:
.. _projectconf_env_platform:
``platform``
@ -265,6 +305,9 @@ using `PlatformIO Embedded Boards Explorer <http://platformio.org/boards>`_.
Board options
~~~~~~~~~~~~~
.. contents::
:local:
``board_mcu``
^^^^^^^^^^^^^
@ -309,8 +352,11 @@ This option isn't available for the all development platforms. The only
Flash chip interface mode. This option isn't available for the all development
platforms. The only :ref:`platform_espressif` supports it.
Building options
~~~~~~~~~~~~~~~~
Build options
~~~~~~~~~~~~~
.. contents::
:local:
.. _projectconf_build_flags:
@ -456,7 +502,7 @@ be applied in theirs order.
By default, ``src_filter`` is predefined to
``+<*> -<.git/> -<svn/> -<example/> -<examples/> -<test/> -<tests/>``,
which means "includes ALL files, then
that means "includes ALL files, then
exclude ``.git`` and ``svn`` repository folders, ``example`` ... folder.
This option can be set by global environment variable
@ -529,8 +575,11 @@ The list with available targets is located in :option:`platformio run --target`.
When no targets are defined, *PlatformIO* will build only sources by default.
Uploading options
~~~~~~~~~~~~~~~~~
Upload options
~~~~~~~~~~~~~~
.. contents::
:local:
.. _projectconf_upload_port:
@ -587,6 +636,9 @@ development platforms. The only :ref:`platform_espressif` supports it.
Library options
~~~~~~~~~~~~~~~
.. contents::
:local:
``lib_install``
^^^^^^^^^^^^^^^
@ -603,12 +655,17 @@ Example:
[env:depends_on_some_libs]
lib_install = 1,13,19
.. _projectconf_lib_force:
``lib_force``
^^^^^^^^^^^^^
Force Library Build System to build specified libraries if they even are not
included in the project source code. Also, these libraries will be processed
in the first order.
.. seealso::
Please make sure to read :ref:`ldf` guide first.
Force Library Dependency Finder to depend on the specified libraries if
they even are not included in the project source code. Also, these
libraries will be processed in the first order.
The correct value for this option is library name (not
folder name). In the most cases, library name is pre-defined in manifest file
@ -627,7 +684,8 @@ Example:
``lib_ignore``
^^^^^^^^^^^^^^
Please make sure to read :ref:`faq_ldf` guides first.
.. seealso::
Please make sure to read :ref:`ldf` guide first.
Specify libraries which should be ignored by Library Dependency Finder.
@ -643,72 +701,14 @@ Example:
[env:ignore_some_libs]
lib_ignore = SPI, Ethernet
.. _projectconf_lib_deep_search:
``lib_deep_search``
^^^^^^^^^^^^^^^^^^^
Please make sure to read :ref:`faq_ldf` guides first.
By default, this option is turned ON (``lib_deep_search = true``) and means
that Library Dependency Finder will analyze ALL source files from the library
and will try automatically find all dependencies.
If you want to disable deep search, please set this option to ``false``.
Note! Some libraries depend on other libraries and the
``#include <...>`` directives for these libraries are not declared in the
"main" header file that is used by upper library. If LDF is turned OFF
(``lib_deep_search = false``), it will not handle these libraries automatically
because it doesn't analyze "each source file" of the nested libraries
(only "nested includes/chain").
For example, there are 2 libraries:
* Library "Foo" with files:
- ``Foo/foo.h``
- ``Foo/foo.cpp``
* Library "Bar" with files:
- ``Bar/bar.h``
- ``Bar/bar.cpp``
:Case 1:
* ``lib_deep_search = false``
* ``Foo/foo.h`` depends on "Bar" library (contains ``#include <bar.h>``)
* ``#include <foo.h>`` is located in one of the project source files
Here are nested includes (``project file > foo.h > bar.h``) and ``LDF`` will
find both libraries "Foo" and "Bar".
:Case 2:
* ``lib_deep_search = false``
* ``Foo/foo.cpp`` depends on "Bar" library (contains ``#include <bar.h>``)
* ``#include <foo.h>`` is located in one of the project source files
In this case, ``LDF`` will not find "Bar" library because it doesn't know
about CPP file (``Foo/foo.cpp``).
:Case 3:
* ``lib_deep_search = true``
* ``Foo/foo.cpp`` depends on "Bar" library (contains ``#include <bar.h>``)
* ``#include <foo.h>`` is located in one of the project source files
Firstly, ``LDF`` finds "Foo" library, then it parses all sources from "Foo"
library and finds ``Foo/foo.cpp`` that depends on ``#include <bar.h>``.
Secondly, it will parse all sources from "Bar" library and this operation
continues until all dependent libraries will not be parsed.
.. _projectconf_lib_extra_dirs:
``lib_extra_dirs``
^^^^^^^^^^^^^^^^^^
Please make sure to read :ref:`faq_ldf` guides first.
.. versionadded:: 3.0
.. seealso::
Please make sure to read :ref:`ldf` guide first.
A list with extra directories/storages where Library Dependency Finder will
look for dependencies. Multiple paths are allowed. Please separate them
@ -729,23 +729,34 @@ Example:
[env:custom_lib_dirs]
lib_extra_dirs = /path/to/private/dir1,/path/to/private/dir2
.. _projectconf_lib_ldf_mode:
``lib_ldf_mode``
^^^^^^^^^^^^^^^^
.. versionadded:: 3.0
.. seealso::
Please make sure to read :ref:`ldf` guide first.
Library Dependency Finder starts work from analyzing source files of the
project (:ref:`projectconf_pio_src_dir`) and can work in the different modes
(see :ref:`ldf_mode`).
By default, this value is set to ``lib_ldf_mode = 2`` and means that LDF
will parse ALL C/C++ source code of the project and will parse ALL C/C++
source code of the each dependent library (recursively).
.. _projectconf_lib_compat_mode:
``lib_compat_mode``
^^^^^^^^^^^^^^^^^^^
Please make sure to read :ref:`faq_ldf` guides first.
.. versionadded:: 3.0
.. seealso::
Please make sure to read :ref:`ldf` guide first.
Library compatibility mode that allows to control Library Dependency Finder
strictness. If library contains manifest file (:ref:`library_config`,
``library.properties``, ``module.json``), then LDF check compatibility of this
library with real build environment. Available compatibility modes:
* ``0`` - don't check for compatibility
* ``1`` - check for the compatibility with :ref:`projectconf_env_framework`
from build environment
* ``2`` - check for the compatibility with :ref:`projectconf_env_framework`
and :ref:`projectconf_env_platform` from build environment.
Library compatibility mode allows to control strictness of Library Dependency
Finder. More details :ref:`ldf_compat_mode`.
By default, this value is set to ``lib_compat_mode = 1`` and means that LDF
will check only for framework compatibility.

View File

@ -14,6 +14,8 @@
platformio test
===============
.. versionadded:: 3.0
.. contents::
Usage

View File

@ -44,7 +44,7 @@ commonvars.AddVariables(
("SRC_FILTER",),
# library options
("LIB_DEEP_SEARCH",),
("LIB_LDF_MODE",),
("LIB_COMPAT_MODE",),
("LIB_IGNORE",),
("LIB_FORCE",),
@ -63,41 +63,35 @@ commonvars.AddVariables(
("UPLOAD_SPEED",),
("UPLOAD_FLAGS",),
("UPLOAD_RESETMETHOD",)
)
) # yapf: disable
DefaultEnvironment(
tools=[
"ar", "as", "gcc", "g++", "gnulink",
"platformio", "devplatform",
"piolib", "piotest", "pioupload", "pioar", "piomisc"
],
], # yapf: disable
toolpath=[join(util.get_source_dir(), "builder", "tools")],
variables=commonvars,
# Propagating External Environment
ENV=environ,
UNIX_TIME=int(time()),
PROGNAME="program",
PIOHOME_DIR=util.get_home_dir(),
PROJECT_DIR=util.get_project_dir(),
PROJECTLIB_DIR=util.get_projectlib_dir(),
PROJECTSRC_DIR=util.get_projectsrc_dir(),
PROJECTTEST_DIR=util.get_projecttest_dir(),
PROJECTDATA_DIR=util.get_projectdata_dir(),
PIOENVS_DIR=util.get_pioenvs_dir(),
BUILD_DIR=join("$PIOENVS_DIR", "$PIOENV"),
PROJECTPIOENVS_DIR=util.get_projectpioenvs_dir(),
BUILD_DIR=join("$PROJECTPIOENVS_DIR", "$PIOENV"),
BUILDSRC_DIR=join("$BUILD_DIR", "src"),
BUILDTEST_DIR=join("$BUILD_DIR", "test"),
LIBSOURCE_DIRS=[
"$PROJECTLIB_DIR",
util.get_lib_dir()
util.get_projectlib_dir(), util.get_projectlibdeps_dir(),
join("$PIOHOME_DIR", "lib")
],
PYTHONEXE=normpath(sys.executable)
)
PYTHONEXE=normpath(sys.executable))
env = DefaultEnvironment()
@ -126,10 +120,9 @@ env.Prepend(LIBSOURCE_DIRS=env.get("LIB_EXTRA_DIRS", []))
env.LoadDevPlatform(commonvars)
env.SConscriptChdir(0)
env.SConsignFile(join("$PIOENVS_DIR", ".sconsign.dblite"))
env.SConsignFile(join("$PROJECTPIOENVS_DIR", ".sconsign.dblite"))
env.SConscript("$BUILD_SCRIPT")
if "UPLOAD_FLAGS" in env:
env.Append(UPLOADERFLAGS=["$UPLOAD_FLAGS"])

View File

@ -194,7 +194,7 @@ class LibBuilderBase(object): # pylint: disable=too-many-instance-attributes
if not search_paths:
search_paths = tuple()
assert isinstance(search_paths, tuple)
deep_search = self.env.get("LIB_DEEP_SEARCH", "true").lower() == "true"
deep_search = int(self.env.get("LIB_LDF_MODE", 2)) == 2
if not self._scanned_paths and (
isinstance(self, ProjectAsLibBuilder) or deep_search):
@ -270,6 +270,10 @@ class LibBuilderBase(object): # pylint: disable=too-many-instance-attributes
"Error: Could not find `%s` dependency for `%s` "
"library\n" % (item['name'], self.name))
self.env.Exit(2)
# when LDF is disabled
if "LIB_LDF_MODE" in self.env and \
int(self.env.get("LIB_LDF_MODE")) == 0:
return
lib_inc_map = {}

View File

@ -109,7 +109,7 @@ class FDSHASumMismatch(PlatformioException):
"is not equal to remote '{2}'"
class NotPlatformProject(PlatformioException):
class NotPlatformIOProject(PlatformioException):
MESSAGE = "Not a PlatformIO project. `platformio.ini` file has not been "\
"found in current working directory ({0}). To initialize new project "\

View File

@ -156,7 +156,7 @@ def _get_projconf_option_dir(name, default=None):
if option_dir.startswith("~"):
option_dir = expanduser(option_dir)
return abspath(option_dir)
except exception.NotPlatformProject:
except exception.NotPlatformIOProject:
pass
return default
@ -180,13 +180,6 @@ def get_home_dir():
return home_dir
def get_lib_dir():
return _get_projconf_option_dir(
"lib_dir",
join(get_home_dir(), "lib")
)
def get_source_dir():
curpath = abspath(__file__)
if not isfile(curpath):
@ -201,29 +194,33 @@ def get_project_dir():
return os.getcwd()
def get_projectsrc_dir():
return _get_projconf_option_dir(
"src_dir",
join(get_project_dir(), "src")
)
def get_projecttest_dir():
return _get_projconf_option_dir(
"test_dir",
join(get_project_dir(), "test")
)
def is_platformio_project(project_dir=None):
if not project_dir:
project_dir = get_project_dir()
return isfile(join(project_dir, "platformio.ini"))
def get_projectlib_dir():
return join(get_project_dir(), "lib")
return _get_projconf_option_dir("lib_dir", join(get_project_dir(), "lib"))
def get_pioenvs_dir():
path = _get_projconf_option_dir(
"envs_dir",
join(get_project_dir(), ".pioenvs")
)
def get_projectlibdeps_dir():
return _get_projconf_option_dir("libdeps_dir",
join(get_project_dir(), ".piolibdeps"))
def get_projectsrc_dir():
return _get_projconf_option_dir("src_dir", join(get_project_dir(), "src"))
def get_projecttest_dir():
return _get_projconf_option_dir("test_dir", join(get_project_dir(),
"test"))
def get_projectpioenvs_dir():
path = _get_projconf_option_dir("envs_dir",
join(get_project_dir(), ".pioenvs"))
if not isdir(path):
os.makedirs(path)
dontmod_path = join(path, "do-not-modify-files-here.url")
@ -247,18 +244,12 @@ def load_project_config(project_dir=None):
if not project_dir:
project_dir = get_project_dir()
if not is_platformio_project(project_dir):
raise exception.NotPlatformProject(project_dir)
raise exception.NotPlatformIOProject(project_dir)
cp = ConfigParser()
cp.read(join(project_dir, "platformio.ini"))
return cp
def is_platformio_project(project_dir=None):
if not project_dir:
project_dir = get_project_dir()
return isfile(join(project_dir, "platformio.ini"))
def change_filemtime(path, time):
os.utime(path, (time, time))
@ -358,6 +349,12 @@ def get_request_defheaders():
)}
@memoized
def _api_request_session():
import requests
return requests.Session()
def get_api_result(path, params=None, data=None, skipdns=False):
import requests
result = None
@ -371,12 +368,14 @@ def get_api_result(path, params=None, data=None, skipdns=False):
try:
if data:
r = requests.post(
r = _api_request_session().post(
url + path, params=params, data=data, headers=headers)
else:
r = requests.get(url + path, params=params, headers=headers)
r.raise_for_status()
r = _api_request_session().get(url + path,
params=params,
headers=headers)
result = r.json()
r.raise_for_status()
except requests.exceptions.HTTPError as e:
if result and "errors" in result:
raise exception.APIRequestError(result['errors'][0]['title'])