diff --git a/platformio/pkgmanager.py b/platformio/pkgmanager.py index bd0e816f..b60332af 100644 --- a/platformio/pkgmanager.py +++ b/platformio/pkgmanager.py @@ -8,28 +8,24 @@ from time import time import click -from platformio import exception, telemetry +from platformio import exception, telemetry, util from platformio.app import get_state_item, set_state_item from platformio.downloader import FileDownloader from platformio.unpacker import FileUnpacker -from platformio.util import get_api_result, get_home_dir, get_systype class PackageManager(object): def __init__(self): - self._package_dir = join(get_home_dir(), "packages") + self._package_dir = join(util.get_home_dir(), "packages") if not isdir(self._package_dir): makedirs(self._package_dir) assert isdir(self._package_dir) @classmethod + @util.memoized def get_manifest(cls): - try: - return cls._cached_manifest - except AttributeError: - cls._cached_manifest = get_api_result("/packages/manifest") - return cls._cached_manifest + return util.get_api_result("/packages/manifest") @staticmethod def download(url, dest_dir, sha1=None): @@ -63,7 +59,7 @@ class PackageManager(object): raise exception.UnknownPackage(name) # check system platform - systype = get_systype() + systype = util.get_systype() builds = ([b for b in manifest[name] if b['system'] == "all" or systype in b['system']]) if not builds: diff --git a/platformio/platforms/base.py b/platformio/platforms/base.py index 9be68191..d91118ec 100644 --- a/platformio/platforms/base.py +++ b/platformio/platforms/base.py @@ -132,32 +132,33 @@ class PlatformFactory(object): return module @classmethod - def get_platforms(cls, installed=False): + @util.memoized + def _lookup_platforms(cls): platforms = {} - - try: - platforms = cls.get_platforms_cache - except AttributeError: - for d in (util.get_home_dir(), util.get_source_dir()): - pdir = join(d, "platforms") - if not isdir(pdir): + for d in (util.get_home_dir(), util.get_source_dir()): + pdir = join(d, "platforms") + if not isdir(pdir): + continue + for p in listdir(pdir): + if (p in ("__init__.py", "base.py") or not + p.endswith(".py")): continue - for p in listdir(pdir): - if (p in ("__init__.py", "base.py") or not - p.endswith(".py")): - continue - type_ = p[:-3] - path = join(pdir, p) - try: - isplatform = hasattr( - cls.load_module(type_, path), - cls.get_clsname(type_) - ) - if isplatform: - platforms[type_] = path - except exception.UnknownPlatform: - pass - cls.get_platforms_cache = platforms + type_ = p[:-3] + path = join(pdir, p) + try: + isplatform = hasattr( + cls.load_module(type_, path), + cls.get_clsname(type_) + ) + if isplatform: + platforms[type_] = path + except exception.UnknownPlatform: + pass + return platforms + + @classmethod + def get_platforms(cls, installed=False): + platforms = cls._lookup_platforms() if not installed: return platforms diff --git a/platformio/util.py b/platformio/util.py index 82582573..a4faf4ca 100644 --- a/platformio/util.py +++ b/platformio/util.py @@ -1,6 +1,8 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. +import collections +import functools import json import os import re @@ -53,6 +55,39 @@ class AsyncPipe(Thread): self.join() +class memoized(object): + ''' + Decorator. Caches a function's return value each time it is called. + If called later with the same arguments, the cached value is returned + (not reevaluated). + https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize + ''' + + def __init__(self, func): + self.func = func + self.cache = {} + + def __call__(self, *args): + if not isinstance(args, collections.Hashable): + # uncacheable. a list, for instance. + # better to not cache than blow up. + return self.func(*args) + if args in self.cache: + return self.cache[args] + else: + value = self.func(*args) + self.cache[args] = value + return value + + def __repr__(self): + '''Return the function's docstring.''' + return self.func.__doc__ + + def __get__(self, obj, objtype): + '''Support instance methods.''' + return functools.partial(self.__call__, obj) + + def get_systype(): data = uname() return ("%s_%s" % (data[0], data[4])).lower() @@ -239,22 +274,24 @@ def get_api_result(path, params=None, data=None): return result -def get_boards(type_=None): +@memoized +def _lookup_boards(): boards = {} - try: - boards = get_boards._cache # pylint: disable=W0212 - except AttributeError: - bdirs = [join(get_source_dir(), "boards")] - if isdir(join(get_home_dir(), "boards")): - bdirs.append(join(get_home_dir(), "boards")) + bdirs = [join(get_source_dir(), "boards")] + if isdir(join(get_home_dir(), "boards")): + bdirs.append(join(get_home_dir(), "boards")) - for bdir in bdirs: - for json_file in os.listdir(bdir): - if not json_file.endswith(".json"): - continue - with open(join(bdir, json_file)) as f: - boards.update(json.load(f)) - get_boards._cache = boards # pylint: disable=W0212 + for bdir in bdirs: + for json_file in os.listdir(bdir): + if not json_file.endswith(".json"): + continue + with open(join(bdir, json_file)) as f: + boards.update(json.load(f)) + return boards + + +def get_boards(type_=None): + boards = _lookup_boards() if type_ is None: return boards @@ -264,33 +301,34 @@ def get_boards(type_=None): return boards[type_] -def get_frameworks(type_=None): +@memoized +def _lookup_frameworks(): frameworks = {} + frameworks_path = join( + get_source_dir(), "builder", "scripts", "frameworks") - try: - frameworks = get_frameworks._cache # pylint: disable=W0212 - except AttributeError: - frameworks_path = join( - get_source_dir(), "builder", "scripts", "frameworks") + frameworks_list = [f[:-3] for f in os.listdir(frameworks_path) + if not f.startswith("__") and f.endswith(".py")] + for _type in frameworks_list: + script_path = join(frameworks_path, "%s.py" % _type) + with open(script_path) as f: + fcontent = f.read() + assert '"""' in fcontent + _doc_start = fcontent.index('"""') + 3 + fdoc = fcontent[ + _doc_start:fcontent.index('"""', _doc_start)].strip() + doclines = [l.strip() for l in fdoc.splitlines() if l.strip()] + frameworks[_type] = { + "name": doclines[0], + "description": " ".join(doclines[1:-1]), + "url": doclines[-1], + "script": script_path + } + return frameworks - frameworks_list = [f[:-3] for f in os.listdir(frameworks_path) - if not f.startswith("__") and f.endswith(".py")] - for _type in frameworks_list: - script_path = join(frameworks_path, "%s.py" % _type) - with open(script_path) as f: - fcontent = f.read() - assert '"""' in fcontent - _doc_start = fcontent.index('"""') + 3 - fdoc = fcontent[ - _doc_start:fcontent.index('"""', _doc_start)].strip() - doclines = [l.strip() for l in fdoc.splitlines() if l.strip()] - frameworks[_type] = { - "name": doclines[0], - "description": " ".join(doclines[1:-1]), - "url": doclines[-1], - "script": script_path - } - get_frameworks._cache = frameworks # pylint: disable=W0212 + +def get_frameworks(type_=None): + frameworks = _lookup_frameworks() if type_ is None: return frameworks