Reference¶
The reddel_server
package provides the complete supported API.
Note
It is not recommended to import any submodules as they are considered protected and might get breaking changes between minor and patch versions.
The API is divided into multiple domains:
Server¶
The Server
is used to provide functions
via EPC that a client like Emacs can call.
The server is automatically created by the reddel
script, which gets installed with
this package. If you need a custom server with more functionality like threading or
authentication (I don’t know what kids need these days) you can use this as a base.
Most functions the server provides come from a provider
(see Providers).
See:
Providers¶
Providers
are the heart of reddel. They provide methods that
you can call remotely via the Server.
list_methods
can be used to get all available
methods of a provider. You can also call it with a piece of source code to get all
methods that can operate on it.
This works by decorating a method with red_validate
and providing some Validators. There are more useful decorators listed below.
The ChainedProvider
is useful for combining multiple providers.
The Server from the CLI uses such provider to combine
a RedBaronProvider
and any provider specified by the user.
By calling add_provider
(also remotely) with a dotted
path you can add your own providers at runtime to extend reddel.
See:
RedBaron Provider¶
The RedBaronProvider
provides the built-in redbaron specific
functionality.
If you want to extend it or write your own provider, it’s recommended to make use of the following decorators:
These decorators are the mini framework that allows the server to tell the client what actions are available for a given piece of code.
There is also a small library with helper functions that might be useful when writing a provider:
See:
Validators¶
Validators are used to get all methods compatible for processing a given source. E.g. if the source is a function, reddel can report to Emacs which functions can be applied to functions and Emacs can use the information to dynamically build a UI.
Validators can transform the source as well.
The transformed source is passed onto the next validator when you use reddel_server.red_validate()
.
All validators provided by reddel_server
can be used as mix-ins.
When you create your own validator and you inherit from multiple builtin ones then
they are effectively combined since all of them perform the appropriate super call.
See:
API¶
-
class
reddel_server.
Server
(server_address=('localhost', 0), RequestHandlerClass=<class epc.handler.EPCHandler>)[source]¶ Bases:
epc.server.EPCServer
EPCServer that provides basic functionality.
This is a simple
epc.server.EPCServer
that exposes methods for clients to call remotely. You can use the python client to connect or call a method from within emacs. The exposed methods are defined by aProvider
. Callreddel_server.Server.set_provider()
to register the functions.If a provider can change it’s methods dynamically, make sure to call
reddel_server.Server.set_provider()
to reset the method cache.Initialize server serving the given address with the given handler.
Parameters: - server_address (tuple) – URL/IP and port
- RequestHandlerClass (
epc.server.EPCHandler
) – the handler class to use
Raises: None
-
allow_reuse_address
= True¶
-
get_provider
()[source]¶ The
reddel_server.ProviderBase
instance that provides methods.
-
set_logging_level
(level)[source]¶ Set logging level
Parameters: level ( str
|int
) – eitherDEBUG
,INFO
,WARNING
,ERROR
,CRITICAL
or integerReturns: None Raises: None Can be called with integer or a string:
>>> import logging >>> import reddel_server >>> server = reddel_server.Server() >>> server.set_logging_level("DEBUG") >>> server.set_logging_level(logging.INFO) >>> server.set_logging_level(10) >>> server.logger.level 10
The string has to be one of the builtin logging levels of
logging
, see Logging Levels.
-
set_provider
(provider)[source]¶ Set the provider and reset the registered functions.
Parameters: provider ( reddel_server.ProviderBase
) – the provider to set
-
class
reddel_server.
ProviderBase
(server)[source]¶ Bases:
object
Base class for all Providers.
A provider exposes methods via the
reddel_server.Server
to clients. By default all public methods (that do not start with an underscore) are exposed.Creating your own basic provider is very simple:
import reddel_server class MyProvider(reddel_server.ProviderBase): def exposed(self): print("I'm exposed") def _private(self): print("I'm private")
server = reddel_server.Server() provider = MyProvider(server) server.set_provider(provider) methods = provider.list_methods() assert "exposed" in methods assert "_private" not in methods
When starting reddel from the command line via the command
reddel
, it’s automatically setup with areddel_server.ChainedProvider
, which combines multiple providers together. It also gives you the ability to callreddel_server.ChainedProvider.add_provider()
from a client.You can get a list of all methods provided by a provider by calling
reddel_server.ProviderBase.list_methods()
.Initialize provider
Parameters: server ( reddel_server.Server
) – the server that is using the provider-
echo
(echo)[source]¶ Echo the given object
Can be used for simple tests. For example to test if certain values can be send to and received from the server.
Parameters: echo – the object to echo Returns: the given echo
-
help
(name)[source]¶ Return the docstring of the method
Parameters: name ( str
) – the name of the method.Example:
import reddel_server server = reddel_server.Server() p = reddel_server.ProviderBase(server) for m in p.list_methods(): print(m + ":") print(p.help(m))
-
list_methods
(src=None)[source]¶ Return a list of methods that this Provider exposes to clients
To get more information for each method use
reddel_server.ProviderBase.help()
.By default this returns all available methods. But it can also be used to only get methods that actually work on a given source. This feature might be handy to dynamically build UIs that adapt to the current context.
To write your own methods that can be filtered in the same way, use the
reddel_server.red_validate()
decorators.Parameters: source ( str
) – ifsrc
return only compatible methodsReturns: list of str
-
server
¶
-
-
class
reddel_server.
ChainedProvider
(server, providers=None)[source]¶ Bases:
reddel_server.provider.ProviderBase
Provider that can chain multiple other providers together
This is the provider used by the command line client to combine
reddel_server.RedBaronProvider
with third party providers.reddel_server.ChainedProvider.add_provider()
is a function to provide a simple plug-in system.Example:
import reddel_server class FooProvider(reddel_server.ProviderBase): def foo(self): pass class BarProvider(reddel_server.ProviderBase): def bar(self): pass server = reddel_server.Server() providers = [FooProvider(server), BarProvider(server)] p = reddel_server.ChainedProvider(server, providers) methods = p.list_methods() assert "foo" in methods assert "bar" in methods
Methods are cached in
reddel_server.ChainedProvider._cached_methods
.reddel_server.ChainedProvider._get_methods()
will use the cached value unless it’sNone
.reddel_server.ChainedProvider.add_provider()
will reset the cache. Keep that in mind when building dynamic providers because the cache might become invalid.Initialize a provider which acts as a combination of the given providers.
Parameters: - server (
reddel_server.Server
) – the server that is using the provider - providers (
list
ofProviderBase
) – list of providers. A provider’s methods at the front of the list will take precedence.
-
add_provider
(dotted_path)[source]¶ Add a new provider
Parameters: dotted_path ( str
) – dotted path to provider class. E.g.mypkg.mymod.MyProvider
.This provides a simple plug-in system. A client (e.g. Emacs) can call
add_provider
with a dotted path to a class within a module. The module has to be importable. So make sure you installed it or added the directory to thePYTHONPATH
.import reddel_server cp = reddel_server.ChainedProvider(reddel_server.Server()) cp.add_provider('reddel_server.RedBaronProvider')
If the given provider has methods with the same name as the existing ones, it’s methods will take precedence.
This will invalidate the cached methods on this instance and also on the server.
- server (
-
class
reddel_server.
ValidatorInterface
[source]¶ Bases:
object
Validator interface
A validator checks a given source input and raises a
ValidationException
if the input is invalid. This can be used to check, which methods of a provider are compatible with a given input (seereddel_server.red_validate()
).Creating your own validator is simple. Just subclass from this class and override
reddel_server.ValidatorInterface.__call__()
.import redbaron import reddel_server class MyValidator(reddel_server.ValidatorInterface): def __call__(self, red, start=None, end=None): if not (start and end): raise reddel_server.ValidationException("Expected a region.") if len(red) != 1: raise reddel_server.ValidationException("Expected only a single root node.") val = MyValidator() val(redbaron.RedBaron("a=2"), reddel_server.Position(1, 1), reddel_server.Position(1, 3)) try: val(redbaron.RedBaron("a=2+1\nb=3")) except reddel_server.ValidationException: pass else: assert False, 'Validator should have raised.'
A Validator can also implement a
transformation
. This transformation is used inreddel_server.red_validate()
.-
__call__
(red, start=None, end=None)[source]¶ Validate the given redbaron source
Parameters: - red (
redbaron.RedBaron
) – the source - start (
reddel_server.Position
|None
) – the start position of the selected region, if any. - end (
reddel_server.Position
|None
) – the end position of the selected region, if any.
Raises: - red (
-
transform
(red, start=None, end=None)[source]¶ Transform the given red baron
The base implementation just returns the source. See
reddel_server.TypeValidator.transform()
for an example.Parameters: - red – a red baron source or other nodes
- start (
reddel_server.Position
|None
) – the start position of the selected region, if any. - end (
reddel_server.Position
|None
) – the end position of the selected region, if any.
Returns: the transformed source, start and end
-
-
class
reddel_server.
OptionalRegionValidator
[source]¶ Bases:
reddel_server.validators.ValidatorInterface
Used for functions that either use the given source code or only the specified region if a region is specified.
If a region is specified the source is transformed to only contain the region.
Examples:
>>> from redbaron import RedBaron >>> import reddel_server >>> val1 = reddel_server.OptionalRegionValidator() >>> src, start, end = val1.transform(RedBaron('def foo(): pass'), start=None, end=None) >>> src.dumps(), start, end ('def foo(): pass\n', None, None) >>> src, start, end = val1.transform(RedBaron('a=1\nb=1'), start=(2,1), end=(2,3)) >>> src.dumps(), start, end ('b=1', Position(row=1, column=1), Position(row=1, column=3))
-
__call__
(red, start=None, end=None)[source]¶ Validate the given redbaron source
Parameters: - red (
redbaron.RedBaron
) – the source - start (
reddel_server.Position
|None
) – the start position of the selected region, if any. - end (
reddel_server.Position
|None
) – the end position of the selected region, if any.
Raises: - red (
-
transform
(red, start=None, end=None)[source]¶ Extract the region from red if any region is specified
Parameters: - red – a red baron source or other nodes
- start (
reddel_server.Position
|None
) – the start position of the selected region, if any. - end (
reddel_server.Position
|None
) – the end position of the selected region, if any.
Returns: the transformed source, start and end
-
-
class
reddel_server.
MandatoryRegionValidator
[source]¶ Bases:
reddel_server.validators.ValidatorInterface
Used for functions that expect a region
-
__call__
(red, start=None, end=None)[source]¶ Validate that a region is specified
Parameters: - red (
redbaron.RedBaron
) – the source - start (
reddel_server.Position
|None
) – the start position of the selected region, if any. - end (
reddel_server.Position
|None
) – the end position of the selected region, if any.
Raises: ValidationException
if start or end isNone
.- red (
-
-
class
reddel_server.
SingleNodeValidator
[source]¶ Bases:
reddel_server.validators.ValidatorInterface
Validate that only one single node is provided.
If a list of nodes is provided, validate that it contains only one element. Transform the source to only a single node.
>>> from redbaron import RedBaron >>> import reddel_server >>> val1 = reddel_server.SingleNodeValidator() >>> val1(redbaron.RedBaron("a=1+1")) >>> val1.transform(redbaron.RedBaron("a=1+1")) (a=1+1, None, None) >>> try: ... val1(redbaron.RedBaron("a=1+1\nb=2")) ... except reddel_server.ValidationException: ... pass ... else: ... assert False, "Validator should have raised"
By default, when creating a
redbaron.RedBaron
source, you always get a list even for a single expression. If you always want the single node, this validator will handle the transformation.-
__call__
(red, start=None, end=None)[source]¶ Validate the given redbaron source
Parameters: - red (
redbaron.RedBaron
) – the source - start (
reddel_server.Position
|None
) – the start position of the selected region, if any. - end (
reddel_server.Position
|None
) – the end position of the selected region, if any.
Raises: - red (
-
transform
(red, start=None, end=None)[source]¶ Extract the single node red is a list
Parameters: - red – a red baron source or other nodes
- start (
reddel_server.Position
|None
) – the start position of the selected region, if any. - end (
reddel_server.Position
|None
) – the end position of the selected region, if any.
Returns: the transformed source, start and end
-
-
class
reddel_server.
TypeValidator
(identifiers)[source]¶ Bases:
reddel_server.validators.ValidatorInterface
Validate that the given source contains the correct type of nodes.
If a region is specified, only the region is checked. If a list of nodes is given, e.g. a
redbaron.RedBaron
object, all nodes will be checked.Examples:
from redbaron import RedBaron import reddel_server val1 = reddel_server.TypeValidator(['def']) # valid val1(RedBaron('def foo(): pass')) val1(RedBaron('def foo(): pass\ndef bar(): pass')) # invalid try: val1(RedBaron('def foo(): pass\na=1+1')) except reddel_server.ValidationException: pass else: assert False, "Validator should have raised"
Initialize the validator
Parameters: identifiers (sequence of str
) – allowed identifiers for the redbaron source-
__call__
(red, start=None, end=None)[source]¶ Validate the given redbaron source
Parameters: - red (
redbaron.RedBaron
) – the source - start (
reddel_server.Position
|None
) – the start position of the selected region, if any. - end (
reddel_server.Position
|None
) – the end position of the selected region, if any.
Raises: - red (
-
-
exception
reddel_server.
ValidationException
[source]¶ Bases:
exceptions.Exception
Raised when calling
reddel_server.ValidatorInterface
and a source is invalid.
-
class
reddel_server.
RedBaronProvider
(server)[source]¶ Bases:
reddel_server.provider.ProviderBase
Provider for inspecting and transforming source code via redbaron.
Initialize provider
Parameters: server ( reddel_server.Server
) – the server that is using the provider-
add_arg
(src, *args, **kwargs)[source]¶ Add a argument at the given index
source input outputs source region only single node allowed types Yes Yes Optional Yes Yes Parameters: - red (
redbaron.RedBaron
) – the red baron source - start (
reddel_server.Position
|None
) – the start position of the selected region, if any. - end (
reddel_server.Position
|None
) – the end position of the selected region, if any. - index (
int
) – position of the argument.0
would mean to put the argument in the front. - arg (
str
) – the argument to add
Returns: the transformed source code
Return type: redbaron.RedBaron
Example:
>>> import reddel_server >>> p = reddel_server.RedBaronProvider(reddel_server.Server()) >>> src = """def foo(arg1, arg2, kwarg2=1): ... arg2 = arg2 or "" ... return arg2 + func(arg1, "arg2 arg2") + kwarg2 ... """ >>> print(p.add_arg(src, start=None, end=None, index=3, arg="kwarg3=123")) def foo(arg1, arg2, kwarg2=1, kwarg3=123): arg2 = arg2 or "" return arg2 + func(arg1, "arg2 arg2") + kwarg2
- red (
-
analyze
(src, *args, **kwargs)[source]¶ Return the red baron help string for the given source
source input outputs source region only single node allowed types Yes No No No Any Parameters: Returns: the help text
Return type: Example:
>>> import reddel_server >>> p = reddel_server.RedBaronProvider(reddel_server.Server()) >>> print(p.analyze("1+1")) BinaryOperatorNode() # identifiers: binary_operator, binary_operator_, binaryoperator, binaryoperatornode value='+' first -> IntNode() # identifiers: int, int_, intnode value='1' second -> IntNode() # identifiers: int, int_, intnode value='1'
-
get_args
(src, *args, **kwargs)[source]¶ Return a list of args and their default value (if any) as source code
source input outputs source region only single node allowed types Yes No Optional Yes def Parameters: - red (
redbaron.RedBaron
) – the red baron source - start (
reddel_server.Position
|None
) – the start position of the selected region, if any. - end (
reddel_server.Position
|None
) – the end position of the selected region, if any.
Returns: list of argument name and default value.
Return type: The default value is always a string, except for arguments without one which will be represented as None.
>>> import reddel_server >>> p = reddel_server.RedBaronProvider(reddel_server.Server()) >>> src = """def foo(arg1, arg2, kwarg1=None, kwarg2=1, kwarg3='None'): ... arg2 = arg2 or "" ... return arg2 + func(arg1, "arg2 arg2") + kwarg2 ... """ >>> p.get_args(src, None, None) [('arg1', None), ('arg2', None), ('kwarg1', 'None'), ('kwarg2', '1'), ('kwarg3', "'None'")]
- red (
-
get_parents
(src, *args, **kwargs)[source]¶ Return a list of parents (scopes) relative to the given position
source input outputs source region only single node allowed types Yes No Mandatory No Any Parameters: - red (
redbaron.RedBaron
) – the red baron source - start (
reddel_server.Position
) – the start position of the selected region. - end (
reddel_server.Position
) – the end position of the selected region.
Returns: a list of parents starting with the element at position first.
Return type: list
of the parents. A parent is represented by aParent
of the type, top-left, bottom-right position. Each position is aPosition
object.>>> import reddel_server >>> import pprint >>> p = reddel_server.RedBaronProvider(reddel_server.Server()) >>> src = """def foo(arg1): ... arg2 = arg2 or "" ... if Ture: ... try: ... pass ... except: ... func(subfunc(arg1="asdf")) ... """ >>> pprint.pprint(p.get_parents(src, reddel_server.Position(7, 32), reddel_server.Position(7, 32))) [Parent(identifier='string', start=Position(row=7, column=31), end=Position(row=7, column=36)), Parent(identifier='call_argument', start=..., end=...), Parent(identifier='call', start=..., end=...), Parent(identifier='call_argument', start=..., end=...), Parent(identifier='call', start=..., end=...), Parent(identifier='atomtrailers', start=..., end=...), Parent(identifier='except', start=..., end=...), Parent(identifier='try', start=..., end=...), Parent(identifier='ifelseblock', start=..., end=...), Parent(identifier='def', start=..., end=...)]
- red (
-
rename_arg
(src, *args, **kwargs)[source]¶ Rename a argument
source input outputs source region only single node allowed types Yes Yes Optional Yes def Parameters: - red (
redbaron.RedBaron
) – the red baron source - start (
reddel_server.Position
|None
) – the start position of the selected region, if any. - end (
reddel_server.Position
|None
) – the end position of the selected region, if any. - oldname (
str
) – name of the argument to rename - newname (
str
) – new name for the argument
Returns: the transformed source code
Return type: redbaron.RedBaron
Example:
>>> import reddel_server >>> p = reddel_server.RedBaronProvider(reddel_server.Server()) >>> src = """def foo(arg1, arg2, kwarg2=1): # arg2 ... arg2 = arg2 or "" ... return arg2 + func(arg1, "arg2 arg2") + kwarg2 ... """ >>> print(p.rename_arg(src, None, None, "arg2", "renamed")) def foo(arg1, renamed, kwarg2=1): # arg2 renamed = renamed or "" return renamed + func(arg1, "arg2 arg2") + kwarg2
- red (
-
-
reddel_server.
red_src
(dump=True)[source]¶ Create decorator that converts the first argument into a red baron source
Parameters: dump ( bool
) – if True, dump the return value from the wrapped function. Expects the return type to be aredbaron.RedBaron
object.Returns: the decorator Return type: types.FunctionType
Example:
import redbaron import reddel_server class MyProvider(reddel_server.ProviderBase): @reddel_server.red_src(dump=False) def inspect_red(self, red): assert isinstance(red, redbaron.RedBaron) red.help() MyProvider(reddel_server.Server()).inspect_red("1+1")
0 ----------------------------------------------------- BinaryOperatorNode() # identifiers: binary_operator, binary_operator_, binaryoperator, binaryoperatornode value='+' first -> IntNode() # identifiers: int, int_, intnode value='1' second -> IntNode() # identifiers: int, int_, intnode value='1'
By default the return value is expected to be a transformed
redbaron.RedBaron
object that can be dumped. This is useful for taking a source as argument, transforming it and returning it back so that it the editor can replace the original source:>>> import redbaron >>> import reddel_server >>> class MyProvider(reddel_server.ProviderBase): ... @reddel_server.red_src(dump=True) ... def echo(self, red): ... assert isinstance(red, redbaron.RedBaron) ... return red >>> MyProvider(reddel_server.Server()).echo("1+1") '1+1'
-
reddel_server.
red_validate
(validators)[source]¶ Create decorator that adds the given validators to the wrapped function
Parameters: validators ( reddel_server.ValidatorInterface
) – the validatorsValidators can be used to provide sanity checks.
reddel_server.ProviderBase.list_methods()
uses them to filter out methods that are incompatible with the given input, which can be used to build dynamic UIs.To use this validator is very simple. Create one or more validator instances (have to inherit from
reddel_server.ValidatorInterface
) and provide them as the argument:import reddel_server validator1 = reddel_server.SingleNodeValidator() validator2 = reddel_server.TypeValidator(["def"]) class MyProvider(reddel_server.ProviderBase): @reddel_server.red_src() @reddel_server.red_validate([validator1, validator2]) def foo(self, red, start, end): assert red.type == 'def' provider = MyProvider(reddel_server.Server()) provider.foo("def bar(): pass", start=None, end=None) # works try: provider.foo("1+1", start=None, end=None) except reddel_server.ValidationException: pass else: assert False, "Validator should have raised"
On top of that validators also can implement a
reddel_server.ValidatorInterface.transform()
function to transform the red source on the way down. The transformed value is passed to the next validator and eventually into the function.See Validators.
-
reddel_server.
redwraps
(towrap)[source]¶ Use this when creating decorators instead of
functools.wraps()
Parameters: towrap ( types.FunctionType
) – the function to wrapReturns: the decorator Return type: types.FunctionType
Makes sure to transfer special reddel attributes to the wrapped function. On top of that uses
functools.wraps()
.Example:
import reddel_server def my_decorator(func): @reddel_server.redwraps(func) # here you would normally use functools.wraps def wrapped(*args, **kwargs): return func(*args, **kwargs) return wrapped
-
class
reddel_server.
Position
[source]¶ Bases:
reddel_server.redlib.Position
Describes a position in the source code by line number and character position in that line.
Parameters:
-
class
reddel_server.
Parent
[source]¶ Bases:
reddel_server.redlib.Parent
Represents a node type with the bounding location.
Parameters:
-
reddel_server.
get_parents
(red)[source]¶ Yield the parents of the given red node
Parameters: red ( redbaron.base_nodes.Node
|redbaron.RedBaron
) – the red baron sourceReturns: each parent of the given node Return type: Generator[ redbaron.base_nodes.Node
]Raises: None
-
reddel_server.
get_node_of_region
(red, start, end)[source]¶ Get the node that contains the given region
Parameters: Returns: the node that contains the region
Return type: redbaron.base_nodes.Node
First the nodes at start and end are gathered. Then the common parent is selected. If the common parent is a list, the minimum slice is used. This makes it easier for the user because he can partially select nodes and still gets what he most likely intended to get.
For example if your region partially selects several lines in a for loop and you want to extract them (
|
shows the region bounds):for i in range(10): a = 1 + 2 b |= 4 c = 5 d =| 7
then we expect to get back:
b = 4 c = 5 d = 7
Note that the leading tab is missing because it doesn’t belong to the
b = 4
node.>>> import redbaron >>> import reddel_server >>> testsrc = ("for i in range(10):\n" ... " a = 1 + 2\n" ... " b = 4\n" ... " c = 5\n" ... " d = 7\n") >>> start = (3, 7) >>> end = (5, 8) >>> reddel_server.get_node_of_region(redbaron.RedBaron(testsrc), start, end).dumps() 'b = 4\n c = 5\n d = 7'
You can also partially select a list:
>>> testsrc = "[1, 2, 3, 4, 5, 6]" >>> start = (1, 8) # "3" >>> end = (1, 14) # "5" >>> reddel_server.get_node_of_region(redbaron.RedBaron(testsrc), start, end).dumps() '3, 4, 5'