
    ViX.              	       r    S r SSKJr  SSKJrJrJrJr  SSKJr  Sr	\	SSSS	S
SSS/	r
S rSS jrS rSS jrg)a  Face Middleware
===============

When using Face's Command framework, Face takes over handling dispatch
of commands and subcommands. A particular command line string is
routed to the configured function, in much the same way that popular
web frameworks route requests based on path.

In more advanced programs, this basic control flow can be enhanced by
adding middleware. Middlewares comprise a stack of functions, each
which calls the next, until finally calling the appropriate
command-handling function. Middlewares are added to the command, with
the outermost middleware being added first. Remember: first added,
first called.

Middlewares are a great way to handle general setup and logic which is
common across many subcommands, such as verbosity, logging, and
formatting. Middlewares can also be used to perform additional
argument validation, and terminate programs early.

The interface of middlewares retains the same injection ability of
Command handler functions. Flags and builtins are automatically
provided. In addition to having its arguments checked against those
available injectables, a middleware _must_ take a ``next_`` parameter
as its first argument. Then, much like a decorator, that ``next_``
function must be invoked to continue to program execution::


  import time

  from face import face_middleware, echo

  @face_middleware
  def timing_middleware(next_):
     start_time = time.time()
     ret = next_()
     echo('command executed in:', time.time() - start_time, 'seconds')
     return ret

As always, code speaks volumes. It's worth noting that ``next_()`` is
a normal function. If you return without calling it, your command's
handler function will not be called, nor will any other downstream
middleware. Another corollary is that this makes it easy to use
``try``/``except`` to build error handling.

While already practical, there are two significant ways it can be
enhanced. The first would be to provide downstream handlers access to
the ``start_time`` value. The second would be to make the echo
functionality optional.

Providing values from middleware
--------------------------------

As mentioned above, the first version of our timing middleware works,
but what if one or more of our handler functions needs to perform a
calculation based on ``start_time``?

Common code is easily folded away by middleware, and we can do so here
by making the start_time available as an injectable::

  import time

  from face import face_middleware, echo

  @face_middleware(provides=['start_time'])
  def timing_middleware(next_):
     start_time = time.time()
     ret = next_(start_time=start_time)
     echo('command executed in:', time.time() - start_time, 'seconds')
     return ret

``start_time`` is added to the list of provides in the middleware
decoration, and ``next_()`` is simply invoked with a ``start_time``
keyword argument. Any command handler function that takes a
``start_time`` keyword argument will automatically pick up the value.

That's all well and fine, but what if we don't always want to know the
duration of the command? Whose responsibility is it to expose that
optional behavior? Lucky for us, middlewares can take care of themselves.

Adding flags to middleware
--------------------------

Right now our middleware changes command output every time it is
run. While that's pretty handy behavior, the command line is all about
options.

We can make our middleware even more reusable by adding self-contained
optional behavior, via a flag::

  import time

  from face import face_middleware, Flag, echo

  @face_middleware(provides=['start_time'],  flags=[Flag('--echo-time', parse_as=True)])
  def timing_middleware(next_, echo_time):
     start_time = time.time()
     ret = next_(start_time=start_time)
     if echo_time:
         echo('command executed in:', time.time() - start_time, 'seconds')
     return ret

Now, every :class:`Command` that adds this middleware will
automatically get a flag, ``--echo-time``. Just like other flags, its
value will be injected into commands that need it.

.. note:: **Weak Dependencies** - Middlewares that set defaults for
          keyword arguments are said to have a "weak" dependency on
          the associated injectable. If the command handler function,
          or another downstream middleware, do not accept the
          argument, the flag will not be parsed, or shown in generated
          help and error messages. This differs from the command
          handler function itself, which will accept arguments even
          when the function signature sets a default.

Wrapping up
-----------

I'd like to say that we were only scratching the surface of
middlewares, but really there's not much more to them. They are an
advanced feature of face, and a very powerful organizing tool for your
code, but like many powerful tools, they are simple. You can use them
in a wide variety of ways. Other useful middleware ideas:

  * Verbosity middleware - provides a ``verbose`` flag for downstream
    commands which can write additional output.
  * Logging middleware - sets up and provides an associated logger
    object for downstream commands.
  * Pipe middleware - Many CLIs are made for streaming. There are some
    semantics a middleware can help with, like breaking pipes.
  * KeyboardInterrupt middleware - Ctrl-C is a common way to exit
    programs, but Python generally spits out an ugly stack trace, even
    where a keyboard interrupt may have been valid.
  * Authentication middleware - provides an AuthenticatedUser object
    after checking environment variables and prompting for a username
    and password.
  * Debugging middleware - Because face middlewares are functions in a
    normal Python stack, it's easy to wrap downstream calls in a
    ``try``/``except``, and add a flag (or environment variable) that
    enables a ``pdb.post_mortem()`` to drop you into a debug console.

The possibilities never end. If you build a middleware of particularly
broad usefulness, consider contributing it back to the core!

    )Flag)
make_chainget_arg_namesget_fbget_callable_labels)injectnext_args_cmd_subcmds_flags_posargs_post_posargs_command_subcommand_c                 J    [        U 5      (       a  [        U SS5      (       a  gg)aN  Mostly for internal use, this function returns True if *target* is
a valid face middleware.

Middlewares can be functions wrapped with the
:func:`face_middleware` decorator, or instances of a user-created
type, as long as it's a callable following face's signature
convention and has the ``is_face_middleware`` attribute set to
True.
is_face_middlewareNTF)callablegetattr)targets    b/home/james-whalen/.local/share/pipx/venvs/semgrep/lib/python3.13/site-packages/face/middleware.pyis_middlewarer      s$     GF,@$GG    Nc                   ^^^ UR                  S/ 5      m[        T[        5      (       a  T/m[        UR                  S/ 5      5      mT(       a,  T H&  n[        U[        5      (       a  M  [        SU-  5      e   UR                  SS5      mU(       a  [        SUR                  5       -  5      eUUU4S jnU (       a  [        U 5      (       a  U" U 5      $ U$ )a  A decorator to mark a function as face middleware, which wraps
execution of a subcommand handler function. This decorator can be
called with or without arguments:

Args:
   provides (list): An optional list of names, declaring which
      values be provided by this middleware at execution time.
   flags (list): An optional list of Flag instances, which will be
      automatically added to any Command which adds this middleware.
   optional (bool): Whether this middleware should be skipped if its 
      provides are not required by the command.

The first argument of the decorated function must be named
"next_". This argument is a function, representing the next
function in the execution chain, the last of which is the
command's handler function.
providesflagszexpected Flag object, not: %roptionalFz unexpected keyword arguments: %rc                 x   > [        U TS9  SU l        [        T5      U l        [        T5      U l        TU l        U $ )N)r   T)check_middlewarer   list_face_flags_face_provides_face_optional)funcr   r   r   s    r   decorate_face_middleware1face_middleware.<locals>.decorate_face_middleware   s:    1"&;"8n&r   )pop
isinstancestrr    r   	TypeErrorkeysr   )r$   kwargsflagr%   r   r   r   s       @@@r   face_middlewarer.      s    $ zz*b)H(C  :GR()EDdD)) ?$ FGG  zz*e,H:V[[]JKK '--##r   c                    Sn[         [        U5      ;   a  [        U[         U4-  5      e[        U5      [        [         /5      -
  nU  Vs/ s H  n[	        UR
                  5      PM     nn[        XX[         5      u  pxn	U	(       aK  S[        U	5      -  n
X[        [        U/ 5      5      -  -  nU(       a  U
S[        U5      -  -  n
[        U
5      eU$ s  snf )ak  Perform basic validation of innermost function, wrap it in
middlewares, and raise a :exc:`NameError` on any unresolved
arguments.

Args:
   middlewares (list): A list of middleware functions, prechecked
      by :func:`check_middleware`.
   innermost (callable): A function to be called after all the
      middlewares.
   preprovided (list): A list of built-in or otherwise preprovided
      injectables.

Returns:
   A single function representing the whole middleware chain.

This function is called automatically by :meth:`Command.prepare()`
(and thus, :meth:`Command.run()`), and is more or less for
internal use.
z1argument %r reserved for middleware use only (%r)z.unresolved middleware or handler arguments: %rz: (%r provided but not resolvable, check middleware order.))	
INNER_NAMEr   	NameErrorsetr    r"   r   sortedsum)middlewares	innermostpreprovided_inner_exc_msgmw_builtinsmwmw_providesmw_chainmw_chain_argsmw_unresmsgavail_unress               r   get_middleware_chainrA      s    ( IN]9--*i)@@AAk"S*%66K5@A[r4))*[KA(2;Yeo(p%HX>AQQCR4H0I"IJP[)* +CnO Bs   
Cc           	         [        U 5      (       d  [        SU -  5      e[        U 5      nSR                  [	        U 5      5      nUR
                  nU(       d  [        SU< S[        < S35      eUS   [        :w  a  [        SU< S[        < SUS   < S	35      eUR                  (       a  [        SU< S
UR                  < S	35      eUR                  (       a  [        SU< SUR                  < S	35      eUb  UOU R                  n[        [        [        5      [        U5      -  5      nU(       a  [        SU< SU< 35      eg)zCheck that a middleware callable adheres to function signature
requirements. Called automatically by
:class:`Command.add_middleware()` and elsewhere, this function
raises :exc:`TypeError` if any issues are found.
z'expected middleware %r to be a function zmiddleware function z" must take at least one argument "z" as its first parameterr   z must take argument "z" as the first parameter, not ""z1 may only take explicitly named arguments, not "*z2 may only take explicitly named arguments, not "**Nz0 provides conflict with reserved face builtins: )r   r*   r   joinr   argsr0   varargsvarkwr"   r    r2   _BUILTIN_PROVIDES)r$   r   fb
func_label	arg_namesconflict_argss         r   r   r     s'    D>>ADHII	B,T23JI%z3 4 	4 |z!%z9Q<A B 	B 
zz9CRZZQ R 	R	xx:DbhhP Q 	Q $/xT5H5HH./#h-?@M9C]T U 	U r   )N)__doc__face.parserr   face.sinterr   r   r   r   r   r0   rI   r   r.   rA   r    r   r   <module>rR      sR   Pf  N N 
&*z?0 
)$X$N!r   