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:

Exceptions

Here is a list of custom exceptions raised in reddel:

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 a Provider. Call reddel_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:
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) – either DEBUG, INFO, WARNING, ERROR, CRITICAL or integer
Returns: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 a reddel_server.ChainedProvider, which combines multiple providers together. It also gives you the ability to call reddel_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) – if src return only compatible methods
Returns:list of str
reddel_version()[source]

Return the reddel version

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’s None. 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 of ProviderBase) – 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 the PYTHONPATH.

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.

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 (see reddel_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 in reddel_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:

ValidationException

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:

ValidationException

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 is None.

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:

ValidationException

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:

ValidationException

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
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:
  • red (redbaron.RedBaron) – the red baron source
  • deep (int) – how deep the nodes get printed
  • with_formatting (bool) – also analyze formatting nodes
Returns:

the help text

Return type:

str

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:

list of tuple with str and str | None in it.

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'")]
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:
Returns:

a list of parents starting with the element at position first.

Return type:

list of the parents. A parent is represented by a Parent of the type, top-left, bottom-right position. Each position is a Position 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=...)]
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
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 a redbaron.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 validators

Validators 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 wrap
Returns: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:
  • row (int) – the line number
  • column (int) – the position in the line
class reddel_server.Parent[source]

Bases: reddel_server.redlib.Parent

Represents a node type with the bounding location.

Parameters:
  • identifier (str) – the node type
  • start (Position) – the position where the node begins in the code
  • end (Position) – the position where the node ends in the code
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 source
Returns: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:
  • red (redbaron.RedBaron) – the red baron source
  • start (Position) – position of the beginning of the region
  • end (Position) – position of the end of the region
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'