예제 #1
0
class NoDiscoveryOptions(DiscoveryOptions):
    """
    A DiscoveryOptions object that will never have hardcoded_discovery or
    serial_filter
    """

    serial_filter = dictobj.Field(sb.overridden(None))
    hardcoded_discovery = dictobj.Field(sb.overridden(None))
예제 #2
0
class Assets(dictobj.Spec):
    src = dictobj.Field(
        sb.overridden("{photons_interactor/static:resource}"),
        formatted=True,
        help="Folder where we can find the source of the assets",
    )

    @property
    def assets_folder(self):
        return os.path.join(self.src, "dist", "static")

    def ensure_npm(self):
        if not shutil.which("npm"):
            raise PhotonsAppError(
                "Couldn't find npm, I suggest you use nvm...")

    @property
    def needs_install(self):
        return (not os.path.exists(os.path.join(self.src, "node_modules"))
                or os.environ.get("REBUILD") == 1)

    def run(self, *args, extra_env=None):
        env = dict(os.environ)
        if extra_env:
            env.update(extra_env)
        subprocess.check_call(["npm", *args], cwd=self.src, env=env)
예제 #3
0
class Assets(dictobj.Spec):
    src = dictobj.Field(
        sb.overridden("{arranger/static:resource}"),
        formatted=True,
        help="Folder where we can find the source of the assets",
    )

    @property
    def dist(self):
        if os.environ.get("NODE_ENV", "production") == "development":
            return os.path.join(self.src, "dist", "dev")
        else:
            return os.path.join(self.src, "dist", "prod")

    def ensure_npm(self):
        if not shutil.which("npm"):
            raise PhotonsAppError("Couldn't find npm, I suggest you use nvm")

    @property
    def needs_install(self):
        return (
            not os.path.exists(os.path.join(self.src, "node_modules"))
            or os.environ.get("REBUILD") == 1
        )

    def run(self, *args, no_node_env=False):
        env = None
        if no_node_env:
            env = dict(os.environ)
            if "NODE_ENV" in env:
                del env["NODE_ENV"]
        subprocess.check_call(["npm", *args], cwd=self.src, **({} if env is None else {"env": env}))
예제 #4
0
    def extra_configuration_collection(self, configuration):
        """
        Hook to do any extra configuration collection or converter registration

        Here we register our base configuration converters:

        photons_app
            .. autoattribute:: photons_app.option_spec.photons_app_spec.PhotonsAppSpec.photons_app_spec

        targets
            .. autoattribute:: photons_app.option_spec.photons_app_spec.PhotonsAppSpec.targets_spec

        target_register
            .. autoattribute:: photons_app.option_spec.photons_app_spec.PhotonsAppSpec.target_register_spec

        protocol_register
            The protocol_register object from photons_messages
        """
        photons_app_spec = PhotonsAppSpec()

        self.register_converters(
            {
                "targets": photons_app_spec.targets_spec,
                "photons_app": photons_app_spec.photons_app_spec,
                "target_register": photons_app_spec.target_register_spec,
                "protocol_register": sb.overridden(protocol_register),
                "reference_resolver_register": photons_app_spec.reference_resolver_register_spec,
            },
            configuration=configuration,
        )
예제 #5
0
파일: database.py 프로젝트: delfick/photons
class Database(dictobj.Spec):
    uri = dictobj.Field(format_into=database_uri_spec(),
                        help="Uri to our database")

    db_migrations = dictobj.Field(
        sb.overridden(
            os.path.join("{interactor:resource}", "database", "migrations")),
        format_into=sb.directory_spec,
    )
예제 #6
0
 def target_register_spec(self):
     """
     Make a TargetRegister object
     """
     return sb.create_spec(
         TargetRegister,
         collector=sb.formatted(
             sb.overridden("{collector}"), formatter=MergedOptionStringFormatter
         ),
     )
예제 #7
0
class Database(dictobj.Spec):
    uri = dictobj.Field(sb.string_spec,
                        wrapper=sb.required,
                        formatted=True,
                        help="Uri to our database")
    db_migrations = dictobj.Field(
        sb.overridden(
            os.path.join("{photons_interactor:resource}", "database",
                         "migrations")),
        format_into=sb.directory_spec,
    )
예제 #8
0
        class Stuff(store.Command):
            three = dictobj.Field(sb.overridden("{wat}"), formatted=True)

            async def execute(self):
                return self
예제 #9
0
 class Thing(store.Command):
     one = dictobj.Field(sb.integer_spec)
     two = dictobj.Field(sb.string_spec)
     three = dictobj.Field(sb.overridden("{wat}"), formatted=True)
예제 #10
0
class TileTransitionOptions(dictobj.Spec):
    background = dictobj.Field(
        sb.overridden(
            BackgroundOption.FieldSpec().empty_normalise(type="current")))
예제 #11
0
 class Thing(dictobj.Spec):
     other = dictobj.Field(sb.overridden("{other}"), formatted=True)
     comms = dictobj.Field(sb.overridden("{comms}"), formatted=True)
예제 #12
0
파일: base.py 프로젝트: xbliss/photons-core
class Target(dictobj.Spec):
    protocol_register = dictobj.Field(sb.overridden("{protocol_register}"),
                                      formatted=True)
    final_future = dictobj.Field(sb.overridden("{final_future}"),
                                 formatted=True)
    description = dictobj.Field(sb.string_spec,
                                default="Base transport functionality")

    item_kls = Item
    script_runner_kls = ScriptRunner

    def session_kls(self, *args, **kwargs):
        raise NotImplementedError()

    @classmethod
    def create(kls, configuration, options=None):
        options = options if options is not None else configuration
        meta = Meta(configuration, []).at("options")
        return kls.FieldSpec(formatter=MergedOptionStringFormatter).normalise(
            meta, options)

    def send(self, msg, reference=None, **kwargs):
        return Sender(self, msg, reference, **kwargs)

    def script(self, raw):
        """Return us a ScriptRunner for the given `raw` against this `target`"""
        items = list(self.simplify(raw))
        if not items:
            items = None
        elif len(items) > 1:
            original = items

            async def gen(*args, **kwargs):
                for item in original:
                    yield item

            items = list(
                self.simplify(FromGenerator(gen, reference_override=True)))[0]
        else:
            items = items[0]
        return self.script_runner_kls(items, target=self)

    def session(self):
        info = {}

        class Session:
            async def __aenter__(s):
                session = info["session"] = await self.make_sender()
                return session

            async def __aexit__(s, exc_type, exc, tb):
                if "session" in info:
                    await self.close_sender(info["session"])

        return Session()

    async def make_sender(self):
        """Create an instance of the sender. This is designed to be shared."""
        return self.session_kls(self)

    # backwards compatibility
    args_for_run = make_sender

    async def close_sender(self, sender):
        """Close a sender"""
        await sender.finish()

    # backwards compatibility
    close_args_for_run = close_sender

    def simplify(self, script_part):
        """
        Used by ``self.script`` to convert ``raw`` into TransportItems

        For each item that is found:

        * Use as is if it already has a run method on it
        * Use item.simplified(self.simplify) if it has a simplified method
        * Otherwise, provide to self.item_kls

        For each leaf child that is found, we gather messages into groups of
        messages without a ``run`` method and yield ``self.item_kls(group)``.

        For example, let's say we have ``[p1, p2, m1, p3]`` where ``m1`` has
        a ``run`` method on it and the others don't, we'll yield:

        * ``self.item_kls([p1, p2])``
        * ``m1``
        * ``self.item_kls([p3])``
        """
        if type(script_part) is not list:
            script_part = [script_part]

        final = []
        for p in script_part:
            if hasattr(p, "run"):
                final.append(p)
            elif hasattr(p, "simplified"):
                final.append(p.simplified(self.simplify))
                continue
            else:
                final.append(p)

        buf = []
        for p in final:
            if not hasattr(p, "run"):
                buf.append(p)
            else:
                if buf:
                    yield self.item_kls(buf)
                    buf = []
                yield p
        if buf:
            yield self.item_kls(buf)
예제 #13
0
class PhotonsApp(dictobj.Spec):
    """
    The main photons_app object.

    .. dictobj_params::
    """

    config = dictobj.Field(
        sb.file_spec, wrapper=sb.optional_spec, help="The root configuration file"
    )
    extra = dictobj.Field(
        sb.string_spec, default="", help="The arguments after the ``--`` in the commandline"
    )
    debug = dictobj.Field(sb.boolean, default=False, help="Whether we are in debug mode or not")
    artifact = dictobj.Field(
        default="", format_into=sb.string_spec, help="The artifact string from the commandline"
    )
    reference = dictobj.Field(
        default="", format_into=sb.string_spec, help="The device(s) to send commands to"
    )
    cleaners = dictobj.Field(
        lambda: sb.overridden([]),
        help="A list of functions to call when cleaning up at the end of the program",
    )
    default_activate = dictobj.NullableField(
        sb.listof(sb.string_spec()), help="A list of photons modules to load by default"
    )
    task_specifier = dictobj.Field(
        sb.delayed(task_specifier_spec()), help="Used to determine chosen task and target"
    )

    @hp.memoized_property
    def final_future(self):
        return self.loop.create_future()

    @hp.memoized_property
    def graceful_final_future(self):
        fut = self.loop.create_future()
        fut.setup = False
        return fut

    @hp.memoized_property
    def loop(self):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        if self.debug:
            loop.set_debug(True)
        return loop

    @hp.memoized_property
    def extra_as_json(self):
        options = "{}" if self.extra in (None, "", sb.NotSpecified) else self.extra

        location = None
        if options.startswith("file://"):
            parsed = urlparse(options)
            location = os.path.abspath(f"{parsed.netloc}{unquote(parsed.path)}")
            if not os.path.exists(location):
                raise BadOption(f"The path {location} does not exist")

            with open(location, "r") as fle:
                options = fle.read()

        try:
            return json.loads(options)
        except (TypeError, ValueError) as error:
            kwargs = {"error": error}
            if location:
                kwargs["read_from"] = location
            raise BadOption("The options after -- wasn't valid json", **kwargs)

    def separate_final_future(self, sleep=0):
        other_future = asyncio.Future()

        def stop():
            other_future.cancel()

        self.loop.remove_signal_handler(signal.SIGTERM)
        self.loop.add_signal_handler(signal.SIGTERM, stop)

        class CM:
            async def __aenter__(s):
                return other_future

            async def __aexit__(s, exc_typ, exc, tb):
                if sleep > 0:
                    await asyncio.sleep(sleep)
                self.final_future.cancel()

        return CM()

    @contextmanager
    def using_graceful_future(self):
        """
        This context manager is used so that a server may shut down before
        the real final_future is stopped.

        This is useful because many photons resources will stop themselves
        when the real final_future is stopped.

        But we may want to stop (say a server) before we run cleanup activities
        and mark final_future as done.

        Usage is like::

            with photons_app.graceful_final_future() as final_future:
                try:
                    await final_future
                except ApplicationStopped:
                    await asyncio.sleep(7)
        """
        final_future = self.final_future

        graceful_future = self.graceful_final_future
        graceful_future.setup = True

        reinstate_handler = False

        if platform.system() != "Windows":

            def stop():
                if not graceful_future.done():
                    graceful_future.set_exception(ApplicationStopped)

            reinstate_handler = self.loop.remove_signal_handler(signal.SIGTERM)
            self.loop.add_signal_handler(signal.SIGTERM, stop)

        yield graceful_future

        # graceful future is no longer in use
        graceful_future.setup = False

        if reinstate_handler:

            def stop():
                if not final_future.done():
                    final_future.set_exception(ApplicationStopped)

            self.loop.remove_signal_handler(signal.SIGTERM)
            self.loop.add_signal_handler(signal.SIGTERM, stop)

    async def cleanup(self, targets):
        for cleaner in self.cleaners:
            try:
                await cleaner()
            except asyncio.CancelledError:
                break
            except KeyboardInterrupt:
                break
            except (RuntimeWarning, Exception):
                exc_info = sys.exc_info()
                log.error(exc_info[1], exc_info=exc_info)

        for target in targets:
            try:
                if hasattr(target, "finish"):
                    await target.finish()
            except asyncio.CancelledError:
                break
            except KeyboardInterrupt:
                break
            except (RuntimeWarning, Exception):
                exc_info = sys.exc_info()
                log.error(exc_info[1], exc_info=exc_info)
예제 #14
0
파일: io.py 프로젝트: delfick/photons
 class Options(dictobj.Spec):
     port = dictobj.NullableField(sb.integer_spec())
     service = dictobj.Field(sb.overridden(Services.UDP))
     state_service = dictobj.Field(sb.overridden(Services.UDP))
예제 #15
0
파일: io.py 프로젝트: delfick/photons
 class Options(dictobj.Spec):
     port = dictobj.Field(sb.overridden(56700))
     service = dictobj.Field(sb.overridden(MemoryService))
     state_service = dictobj.Field(sb.overridden(Services.UDP))