Public API¶
All of the public functions and classes of repobee-plug
can be imported
from the top-level package repobee_plug
. Only this API is stable, the
internal modules currently are not. The recommended way to use repobee_plug
is to import it either as is, or alias it to plug
. Typically, I do the
latter.
import repobee_plug as plug
def act(path, api):
result = plug.Result(
name="hello",
msg="Hello, world!",
status=plug.Status.SUCCESS,
)
return result
@plug.repobee_hook
def clone_task():
"""A hello world clone task."""
return plug.Task(act=act)
Hook functions¶
There are two parts to hook functions in RepoBee: the specifications of the hook functions, and the implementation markers to signify that you have (attempted) to implement a hook.
Implementation markers¶
There are two ways to mark a function as a hook implementation: with the
repobee_hook
decorator or using the Plugin
class.
-
class
repobee_plug.
Plugin
[source]¶ This is a base class for plugin classes. For plugin classes to be picked up by RepoBee, they must inherit from this class.
Public methods must be hook methods. If there are any public methods that are not hook methods, an error is raised on creation of the class. As long as the method has the correct name, it will be recognized as a hook method during creation. However, if the signature is incorrect, the plugin framework will raise a runtime exception once it is called. Private methods (i.e. methods prefixed with
_
) carry no restrictions.The signatures of hook methods are not checked until the plugin class is registered by the
repobee_plug.manager
(an instance ofpluggy.manager.PluginManager
). Therefore, when testing a plugin, it is a good idea to include a test where it is registered with the manager to ensure that it has the correct signatures.A plugin class is instantiated exactly once; when RepoBee loads the plugin. This means that any state that is stored in the plugin will be carried throughout the execution of a RepoBee command. This makes plugin classes well suited for implementing tasks that require command line options or configuration values, as well as for implementing extension commands.
Extension hooks¶
Important
The hook function specifications are part of the public API for documentation purposes only. You should not import or use these function in any way, but only implement them as described in Implementing hook functions.
Hookspecs for repobee extension hooks.
Extension hooks add something to the functionality of repobee, but are not necessary for its operation. Currently, all extension hooks are related to cloning repos.
-
class
repobee_plug._exthooks.
CloneHook
[source] Hook functions related to cloning repos.
-
act_on_cloned_repo
(path, api)[source] Do something with a cloned repo.
Deprecated since version 0.12.0: This hook is has been replaced by
TaskHooks.clone_task()
. Once all known, existing plugins have been migrated to the new hook, this hook will be removed.Parameters: Return type: Optional
[Result
]Returns: optionally returns a Result namedtuple for reporting the outcome of the hook. May also return None, in which case no reporting will be performed for the hook.
-
clone_parser_hook
(clone_parser)[source] Do something with the clone repos subparser before it is used used to parse CLI options. The typical task is to add options to it.
Deprecated since version 0.12.0: This hook is has been replaced by
TaskHooks.clone_task()
. Once all known, existing plugins have been migrated to the new hook, this hook will be removed.Parameters: clone_parser ( ArgumentParser
) – Theclone
subparser.Return type: None
-
config_hook
(config_parser)[source] Hook into the config file parsing.
Parameters: config – the config parser after config has been read. Return type: None
-
-
class
repobee_plug._exthooks.
ExtensionCommandHook
[source] Hooks related to extension commands.
-
create_extension_command
()[source] Create an extension command to add to the RepoBee CLI. The command will be added as one of the top-level subcommands of RepoBee. This hook is called precisely once, and should return an
ExtensionCommand
.def command(args: argparse.Namespace, api: apimeta.API)
The
command
function will be called if the extension command is used on the command line.Note that the
RepoBeeExtensionParser
class is just a thin wrapper aroundargparse.ArgumentParser
, and can be used in an identical manner. The following is an example definition of this hook that adds a subcommand calledexample-command
, that can be called withrepobee example-command
.import repobee_plug as plug def callback(args: argparse.Namespace, api: plug.API) -> None: LOGGER.info("callback called with: {}, {}".format(args, api)) @plug.repobee_hook def create_extension_command(): parser = plug.RepoBeeExtensionParser() parser.add_argument("-b", "--bb", help="A useless argument") return plug.ExtensionCommand( parser=parser, name="example-command", help="An example command", description="Description of an example command", callback=callback, )
Important
If you need to use the api, you set
requires_api=True
in theExtensionCommand
. This will automatically add the options that the API requires to the CLI options of the subcommand, and initialize the api and pass it in.See the documentation for
ExtensionCommand
for more details on it.Return type: ExtensionCommand
Returns: A ExtensionCommand
.
-
-
class
repobee_plug._exthooks.
TaskHooks
[source] Hook functions relating to RepoBee tasks.
-
clone_task
()[source] Create a task to run on a copy of a cloned student repo. This hook replaces the old
act_on_cloned_repo
hook.Implementations of this hook should return a
Task
, which defines a callback that is called after all student repos have been cloned. See the definition ofTask
for details.Return type: Task
Returns: A Task
instance defining a RepoBee task.
-
setup_task
()[source] Create a task to run on a copy of the master repo before it is pushed out to student repositories. This can for example be pre-flight checks of the master repo, or something else entirely.
Implementations of this hook should return a
Task
, which defines a callback that is called after the master repo has been safely copied, but before that copy is pushed out to student repositories. Note that any changes to the repository must be committed to actually show up in the student repositories.Note
Structural changes to the master repo are not currently supported. Changes to the repository during the callback will not be reflected in the generated repositories. Support for preprocessing (such that changes do take effect) is a potential future feature.
Return type: Task
-
Core hooks¶
Important
The hook function specifications are part of the public API for documentation purposes only. You should not import or use these function in any way, but only implement them as described in Implementing hook functions.
Hookspecs for repobee core hooks.
Core hooks provide the basic functionality of repobee. These hooks all have default implementations, but are overridden by any other implementation. All hooks in this module should have the firstresult=True option to the hookspec to allow for this dynamic override.
-
class
repobee_plug._corehooks.
APIHook
[source] Hooks related to platform APIs.
-
api_init_requires
()[source] Return which of the arguments to apimeta.APISpec.__init__ that the given API requires. For example, the GitHubAPI requires all, but the GitLabAPI does not require
user
.Return type: Tuple
[str
]Returns: Names of the required arguments.
-
get_api_class
()[source] Return an API platform class. Must be a subclass of apimeta.API.
Returns: An apimeta.API subclass.
-
-
class
repobee_plug._corehooks.
PeerReviewHook
[source] Hook functions related to allocating peer reviews.
-
generate_review_allocations
(teams, num_reviews)[source] Generate
ReviewAllocation
tuples from the provided teams, given that this concerns reviews for a single master repo.The provided teams of students should be treated as units. That is to say, if there are multiple members in a team, they should always be assigned to the same review team. The best way to merge two teams
team_a
andteam_b
into one review team is to simply do:team_c = apimeta.Team(members=team_a.members + team_b.members)
This can be scaled to however many teams you would like to merge. As a practical example, if teams
team_a
andteam_b
are to reviewteam_c
, then the followingReviewAllocation
tuple, here calledallocation
, should be contained in the returned list.review_team = apimeta.Team(members=team_a.members + team_b.members) allocation = containers.ReviewAllocation( review_team=review_team, reviewed_team=team_c, )
Note
Respecting the
num_reviews
argument is optional: only do it if it makes sense. It’s good practice to issue a warning if num_reviews is ignored, however.Parameters: - team – A list of
Team
tuples. - num_reviews (
int
) – Amount of reviews each student should perform (and consequently amount of reviewers per repo)
Return type: List
[ReviewAllocation
]Returns: - A list of
ReviewAllocation
tuples.
- team – A list of
-
API Wrappers¶
The API wrappers in repobee-plug
provide a level of abstraction from the
the underlying platform API (e.g. GitHub or GitLab), and allows RepoBee to work
with different platforms. To fully support a new platform, the
API
must be subclassed an all of its functions
implemented. It is possible to support a subset of the functionality as well,
but you will need to look into the RepoBee implementation to see which
API methods are required for which commands.
-
class
repobee_plug.
TeamPermission
[source]¶ Enum specifying team permissions on creating teams. On GitHub, for example, this can be e.g. push or pull.
-
class
repobee_plug.
Issue
[source]¶ Wrapper class for an Issue API object.
-
class
repobee_plug.
API
(base_url, token, org_name, user)[source]¶ API base class that all API implementations should inherit from. This class functions similarly to an abstract base class, but with a few key distinctions that affect the inheriting class.
- Public methods must override one of the public methods of
APISpec
. If an inheriting class defines any other public method, anAPIError
is raised when the class is defined. - All public methods in
APISpec
have a default implementation that simply raise aNotImplementedError
. There is no requirement to implement any of them.
-
add_repos_to_review_teams
(team_to_repos, issue=None)¶ Add repos to review teams. For each repo, an issue is opened, and every user in the review team is assigned to it. If no issue is specified, sensible defaults for title and body are used.
Parameters: Return type: None
-
close_issue
(title_regex, repo_names)¶ Close any issues in the given repos in the target organization, whose titles match the title_regex.
Parameters: Return type: None
-
create_repos
(repos)¶ Create repos in the target organization according the those specced by the
repos
argument. Repos that already exist are skipped.Parameters: repos ( Iterable
[Repo
]) – Repos to be created.Return type: List
[str
]Returns: A list of urls to the repos specified by the repos
argument, both those that were created and those that already existed.
-
delete_teams
(team_names)¶ Delete all teams in the target organizatoin that exactly match one of the provided
team_names
. Skip any team name for which no match is found.Parameters: team_names ( Iterable
[str
]) – A list of team names for teams to be deleted.Return type: None
-
discover_repos
(teams)¶ Return all repositories related to the provided teams.
Parameters: teams ( Iterable
[Team
]) – Team tuples.Return type: Generator
[Repo
,None
,None
]Returns: A list of Repo tuples.
-
ensure_teams_and_members
(teams, permission=<TeamPermission.PUSH: 'push'>)¶ Ensure that the teams exist, and that their members are added to the teams.
Teams that do not exist are created, teams that already exist are fetched. Members that are not in their teams are added, members that do not exist or are already in their teams are skipped.
Parameters: - teams (
Iterable
[Team
]) – A list of teams specifying student groups. - permission (
TeamPermission
) – The permission these teams (or members of them) should be given in regards to associated repositories.
Return type: List
[Team
]Returns: A list of Team API objects of the teams provided to the function, both those that were created and those that already existed.
- teams (
-
extract_repo_name
(repo_url)¶ Extract a repo name from the provided url.
Parameters: repo_url ( str
) – A URL to a repository.Return type: str
Returns: The name of the repository corresponding to the url.
-
get_issues
(repo_names, state=<IssueState.OPEN: 'open'>, title_regex='')¶ Get all issues for the repos in repo_names an return a generator that yields (repo_name, issue generator) tuples. Will by default only get open issues.
Parameters: Return type: Generator
[Tuple
[str
,Generator
[Issue
,None
,None
]],None
,None
]Returns: A generator that yields (repo_name, issue_generator) tuples.
-
get_repo_urls
(master_repo_names, org_name=None, teams=None)¶ Get repo urls for all specified repo names in the organization. As checking if every single repo actually exists takes a long time with a typical REST API, this function does not in general guarantee that the urls returned actually correspond to existing repos.
If the
org_name
argument is supplied, urls are computed relative to that organization. If it is not supplied, the target organization is used.If the teams argument is supplied, student repo urls are computed instead of master repo urls.
Parameters: Return type: Returns: a list of urls corresponding to the repo names.
-
get_review_progress
(review_team_names, teams, title_regex)¶ Get the peer review progress for the specified review teams and student teams by checking which review team members have opened issues in their assigned repos. Only issues matching the title regex will be considered peer review issues. If a reviewer has opened an issue in the assigned repo with a title matching the regex, the review will be considered done.
Note that reviews only count if the student is in the review team for that repo. Review teams must only have one associated repo, or the repo is skipped.
Parameters: Return type: Returns: a mapping (reviewer -> assigned_repos), where reviewer is a str and assigned_repos is a
_repobee.tuples.Review
.
-
get_teams
()¶ Get all teams related to the target organization.
Return type: List
[Team
]Returns: A list of Team API object.
-
open_issue
(title, body, repo_names)¶ Open the specified issue in all repos with the given names, in the target organization.
Parameters: Return type: None
-
static
verify_settings
(user, org_name, base_url, token, master_org_name=None)¶ Verify the following (to the extent that is possible and makes sense for the specifi platform):
- Base url is correct
- The token has sufficient access privileges
- Target organization (specifiend by
org_name
) exists - If master_org_name is supplied, this is also checked to exist.
- Target organization (specifiend by
- User is owner in organization (verify by getting
- If master_org_name is supplied, user is also checked to be an owner of it.
organization member list and checking roles)
Should raise an appropriate subclass of
_repobee.exception.APIError
when a problem is encountered.Parameters: Returns: True if the connection is well formed.
Raises:
- Public methods must override one of the public methods of
Containers¶
The containers in repobee-plug
are immutable classes for storing data.
Probably the most important containers are the
Result
and the Task
classes.
-
class
repobee_plug.
Result
(name, status, msg, data=None)[source]¶ Container for storing results from hooks.
Parameters: - name (
str
) – Name to associate with this result. This is typically the name of the plugin that returns this result. - status (
Status
) – Status of the plugin execution. - msg (
str
) – A free-form result message. - data (
Optional
[Mapping
[Any
,Any
]]) – Semi-structured data in the form of a dictionary. All of the contents of the dictionary should be serializable as this is primarily used for JSON storage.
- name (
-
class
repobee_plug.
Task
(act, add_option=None, handle_args=None, persist_changes=False)[source]¶ A data structure for describing a task. Tasks are operations that plugins can define to run on for example cloned student repos (a clone task) or on master repos before setting up student repos (a setup task). Only the
act
attribute is required, all other attributes can be omitted.The callback methods should have the following headers.
def act( path: pathlib.Path, api: repobee_plug.API ) -> Optional[containers.Result]: def add_option(parser: argparse.ArgumentParser) -> None: def handle_args(args: argparse.Namespace) -> None:
Note
The functions are called in the following order:
add_option
->handle_args
->act
.Important
The
act
callback should never change the Git repository it acts upon (e.g. running commands such asgit add
,git checkout
orgit commit
). This can have adverse and unexpected effects on RepoBee’s functionality. It is however absolutely fine to change the files in the Git working tree, as long as nothing is added or committed.Each callback is called at most once. They are not guaranteed to execute, because there may be an unexpected crash somewhere else, or the plugin may not come into scope (for example, a clone task plugin will not come into scope if
repobee setup
is run). The callbacks can do whatever is appropriate for the plugin, except for changing any Git repositories. For information on the types used in the callbacks, see the Python stdlib documentation forargparse
.As an example, a simple clone task can be defined like so:
import repobee_plug as plug def act(path, api): return plug.Result( name="example", msg="IT LIVES!", status=plug.Status.SUCCESS ) @plug.repobee_hook def clone_task(): return plug.Task(act=act)
If your task plugin also needs to access the configuration file, then implement the separate
config_hook
hook. For more elaborate instructions on creating tasks, see the tutorial.Parameters: - act (
Callable
[[Path
,API
],Result
]) – A required callback function that takes the path to a repository worktree and an API instance, and optionally returns a Result to report results. - add_option (
Optional
[Callable
[[ArgumentParser
],None
]]) – An optional callback function that adds options to the CLI parser. - handle_args (
Optional
[Callable
[[Namespace
],None
]]) – An optional callback function that receives the parsed CLI args. - persist_changes (
bool
) – If True, the task requires that changes to the repository that has been acted upon be persisted. This means different things in different contexts (e.g. whether the task is executed in a clone context or in a setup context), and may not be supported for all contexts.
- act (
-
class
repobee_plug.
Deprecation
¶ Parameters: Create new instance of Deprecation(replacement, remove_by_version)
-
remove_by_version
¶ Alias for field number 1
-
replacement
¶ Alias for field number 0
-
-
class
repobee_plug.
Status
[source]¶ Status codes enums for Results.
-
SUCCESS
¶ Signifies a plugin execution without any complications.
-
WARNING
¶ Signifies a plugin execution with non-critical failures.
-
ERROR
¶ Signifies a critical error during execution.
-
-
class
repobee_plug.
ExtensionCommand
(parser, name, help, description, callback, requires_api=False, requires_base_parsers=None)[source]¶ Class defining an extension command for the RepoBee CLI.
Parameters: - parser (
ExtensionParser
) – The parser to use for the CLI. - name (
str
) – Name of the command. - help (
str
) – Text that will be displayed when runningrepobee -h
- description (
str
) – Text that will be displayed when calling the-h
option for this specific command. Should be elaborate in describing the usage of the command. - callback (
Callable
[[Namespace
,Optional
[API
]],Optional
[Mapping
[str
,Result
]]]) – A callback function that is called if this command is used on the CLI. It is passed the parsed namespace and the platform API. It may optionally return a result mapping on the form (name: str -> List[Result]) that’s reported by RepoBee. - requires_api (
bool
) – If True, the base arguments required for the platform API are added as options to the extension command, and the platform API is then passed to the callback function. It is then important not to have clashing option names. If False, the base arguments are not added to the CLI, and None is passed in place of the API. If you includeREPO_DISCOVERY
inrequires_base_parsers
, then you must set this to True. - requires_base_parsers (
Optional
[Iterable
[BaseParser
]]) – A list ofrepobee_plug.BaseParser
that decide which base parsers are added to this command. For example, settingrequires_base_parsers = [BaseParser.STUDENTS]
adds the--students
and--students-file
options to this extension command’s parser.
- parser (
-
class
repobee_plug.
ExtensionParser
[source]¶ An ArgumentParser specialized for RepoBee extension commands.
-
add_argument
(dest, ..., name=value, ...)¶ add_argument(option_string, option_string, …, name=value, …)
-
error
(message: string)¶ Prints a usage message incorporating the message to stderr and exits.
If you override this in a subclass, it should not return – it should either exit or raise an exception.
-
Helpers¶
repobee-plug
defines various helper functions and classes for use in both
RepoBee core and in plugins. These vary from generating repo names, to handling
deprecation, to mapping key data structures from and to JSON.
-
repobee_plug.
json_to_result_mapping
(json_string)[source]¶ Deserialize a JSON string to a mapping
repo_name: str -> hook_results: List[Result]
Return type: Mapping
[str
,List
[Result
]]
-
repobee_plug.
result_mapping_to_json
(result_mapping)[source]¶ Serialize a result mapping
repo_name: str -> hook_results: List[Result]
to JSON.Return type: str
-
repobee_plug.
generate_repo_name
(team_name, master_repo_name)[source]¶ Construct a repo name for a team.
Parameters: Return type:
-
repobee_plug.
generate_repo_names
(team_names, master_repo_names)[source]¶ Construct all combinations of generate_repo_name(team_name, master_repo_name) for the provided team names and master repo names.
Parameters: Return type: Returns: a list of repo names for all combinations of team and master repo.
Exceptions¶
-
exception
repobee_plug.
PlugError
(*args, **kwargs)[source]¶ Base class for all repobee_plug exceptions.
Instantiate a PlugError.
Parameters: - args – List of positionals. These are passed directly to
Exception
. Typically, you should only pass an error message here. - kwargs – Keyword arguments to indicate what went wrong.
For example, if the argument
a
caused the error, then you should passa=a
as a kwarg so it can be introspected at a later time.
- args – List of positionals. These are passed directly to
-
exception
repobee_plug.
ExtensionCommandError
(*args, **kwargs)[source]¶ Raise when an :py:class:~repobee_plug.containers.ExtensionCommand: is incorrectly defined.
Instantiate a PlugError.
Parameters: - args – List of positionals. These are passed directly to
Exception
. Typically, you should only pass an error message here. - kwargs – Keyword arguments to indicate what went wrong.
For example, if the argument
a
caused the error, then you should passa=a
as a kwarg so it can be introspected at a later time.
- args – List of positionals. These are passed directly to
-
exception
repobee_plug.
HookNameError
(*args, **kwargs)[source]¶ Raise when a public method in a class that inherits from
Plugin
does not have a hook name.Instantiate a PlugError.
Parameters: - args – List of positionals. These are passed directly to
Exception
. Typically, you should only pass an error message here. - kwargs – Keyword arguments to indicate what went wrong.
For example, if the argument
a
caused the error, then you should passa=a
as a kwarg so it can be introspected at a later time.
- args – List of positionals. These are passed directly to