APIο
π§ this page is still a work in progress π§
If youβd like to make a contribution, please join the project on GitHub
Personioο
- class personio_py.Personio(base_url: Optional[str] = None, client_id: Optional[str] = None, client_secret: Optional[str] = None, dynamic_fields: Optional[List[personio_py.mapping.DynamicMapping]] = None, session: Optional[requests.sessions.Session] = None)[source]ο
Bases:
object
the Personio API client.
- Parameters
base_url β use this custom base URL instead of the default https://api.personio.de/v1/
client_id β the client id for API authentication (if not provided, defaults to the
CLIENT_ID
environment variable)client_secret β the client secret for API authentication (if not provided, defaults to the
CLIENT_SECRET
environment variable)dynamic_fields β definition of expected dynamic fields. List of
DynamicMapping
tuples.
- ABSENCE_URL = 'company/time-offs'ο
- ATTENDANCE_URL = 'company/attendances'ο
- PROJECT_URL = 'company/attendances/projects'ο
- authenticate()[source]ο
Try to authenticate (using Personioβs
/auth
endpoint) with the credentials (client ID and secret) that were provided to this instance.There should be no need to explicitly call this function, because you will be automatically authenticated when you make your first request.
If the authentication was successful, the authentication token is stored in the headers dictionary (
self.headers
). This token will be sent with every request and it will be automatically rotated whenever necessary. If the authentication failed, aPersonioApiError
will be raised.
- create_absence(absence: personio_py.models.Absence) personio_py.models.Absence [source]ο
Creates an absence record on the Personio servers
- Parameters
absence β The absence object to be created
- Raises
PersonioError β If the absence could not be created on the Personio servers
- create_attendances(attendances: List[personio_py.models.Attendance]) bool [source]ο
Create all given attendance records.
Note: If one or more attendances can not be created, other attendances will be created but their corresponding objects passed as attendances will not be updated.
- Parameters
attendances β A list of attendance records to be created.
- create_employee(employee: personio_py.models.Employee, refresh=True) personio_py.models.Employee [source]ο
placeholder; not ready to be used
- create_project(project: personio_py.models.Project) personio_py.models.Project [source]ο
Creates a project record on the Personio servers.
- Parameters
project β The project object to be created
- Raises
PersonioError β If the project could not be created on the Personio servers
- delete_absence(absence: Union[personio_py.models.Absence, int])[source]ο
Delete an existing record
An absence id is required.
- Parameters
absence β The Absence object holding the new data or an absence record id to delete.
- Raises
ValueError β If a query is required but not allowed or the query does not provide exactly one result.
- delete_attendance(attendance: personio_py.models.Attendance)[source]ο
Delete an existing record
Either an attendance id or o remote query is required. Remote queries are only executed if required. An Attendance object returned by get_attendances() include the attendance id. DO NOT SET THE ID YOURSELF.
- Parameters
attendance β The Attendance object holding the new data or an attendance record id to delete.
- Raises
ValueError: If a query is required but not allowed or the query does not provide exactly one result.
- delete_project(project: Union[personio_py.models.Project, int]) None [source]ο
Deletes a project record on the Personio servers.
- Parameters
project β The project object to be updated
- Raises
ValueError β If a query is required but not allowed or the query does not provide exactly one result.
- get_absence(absence: Union[personio_py.models.Absence, int]) personio_py.models.Absence [source]ο
Get an absence record from a given id.
- Parameters
absence β The absence id to fetch.
- get_absence_types() List[personio_py.models.AbsenceType] [source]ο
Get a list of all available absence types, e.g. βpaid vacationβ or βparental leaveβ.
The absence types are used to classify the absences of employees (see
get_absences
to get a list of all absences for the employees). EachAbsence
also contains theAbsenceType
for this instance; the purpose of this function is to provide you with a list of all possible options that can show up.
- get_absences(employees: Union[int, List[int], personio_py.models.Employee, List[personio_py.models.Employee]], start_date: Optional[datetime.datetime] = None, end_date: Optional[datetime.datetime] = None) List[personio_py.models.Absence] [source]ο
Get a list of all absence records for the employees with the specified IDs.
Note that internally, multiple requests may be made by this function due to limitations of the Personio API: Only a limited number of records can be retrieved in a single request and only a limited number of employee IDs can be passed as URL parameters. The results are still presented as a single list, no matter how many requests are made.
- Parameters
employees β a single employee or a list of employee objects or IDs. Absence records for all matching employees will be retrieved.
start_date β only return absence records from this date (inclusive, optional)
end_date β only return absence records up to this date (inclusive, optional)
- Returns
list of
Absence
records for the specified employees
- get_attendances(employees: Union[int, List[int], personio_py.models.Employee, List[personio_py.models.Employee]], start_date: Optional[datetime.datetime] = None, end_date: Optional[datetime.datetime] = None) List[personio_py.models.Attendance] [source]ο
Get a list of all attendance records for the employees with the specified IDs
Note that internally, multiple requests may be made by this function due to limitations of the Personio API: Only a limited number of records can be retrieved in a single request and only a limited number of employee IDs can be passed as URL parameters. The results are still presented as a single list, no matter how many requests are made.
- Parameters
employees β a single employee or a list of employee objects or IDs. Attendance records for all matching employees will be retrieved.
start_date β only return attendance records from this date (inclusive, optional)
end_date β only return attendance records up to this date (inclusive, optional)
- Returns
list of
Attendance
records for the specified employees
- get_employee(employee_id: int) personio_py.models.Employee [source]ο
Get a single employee with the specified ID. Does not involve pagination.
- Parameters
employee_id β the Personio ID of the employee to fetch
- Returns
an
Employee
instance or a PersonioApiError, if the employee does not exist
- get_employee_picture(employee: Union[int, personio_py.models.Employee], width: Optional[int] = None) Optional[bytes] [source]ο
Get the profile picture of the specified employee as image file (usually png or jpg).
- Parameters
employee β get the picture of this employee or the employee with the specified Personio ID
width β optionally scale the profile picture to this width. Defaults to the original width of the profile picture.
- Returns
the profile picture as png or jpg file (bytes)
- get_employees() List[personio_py.models.Employee] [source]ο
Get a list of all employee records in your account. Does not involve pagination.
- Returns
list of
Employee
instances
- get_projects() List[personio_py.models.Project] [source]ο
Get a list of all company projects.
- Returns
list of
Project
records
- invalidate_index()[source]ο
Invalidates the search index. New data will be requested on the next search.
- request(path: str, method='GET', params: Optional[Dict[str, Any]] = None, data: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, auth_rotation=True) requests.models.Response [source]ο
Make a request against the Personio API. Returns the HTTP response, which might be successful or not.
Rotation of authorization tokens is handled in this function. If no token is available (first request of this instance), the
authenticate()
function is called. On subsequent requests, the new authorization token is read from the response headers and replaces the previous one. Note that this behavior can be turned off by setting auth_rotation=False, which is required for some requests that do not return a new authorization token.- Parameters
path β the URL path for this request (relative to the Personio API base URL)
method β the HTTP request method (default: GET)
params β dictionary of URL parameters (optional)
data β dictionary of data to send in the request body (optional)
headers β additional request headers (authentication is handled separately)
auth_rotation β set to True, if authentication keys should be rotated during this request (default: True)
- Returns
the HTTP response (the caller is responsible for handling HTTP errors)
- request_image(path: str, method='GET', params: Optional[Dict[str, Any]] = None, auth_rotation=False) Optional[bytes] [source]ο
Request an image file (as png or jpg) from the Personio API. Returns the image as byte array, or None, if no image is available for this resource (HTTP status 404). When any other errors occur, a PersonioApiError is raised.
- Parameters
path β the URL path for this request (relative to the Personio API base URL)
method β the HTTP request method (default: GET)
params β dictionary of URL parameters (optional)
auth_rotation β set to True, if authentication keys should be rotated during this request (default: False for image requests)
- Returns
the image (bytes) or None, if no image is available
- request_json(path: str, method='GET', params: Optional[Dict[str, Any]] = None, data: Optional[Dict[str, Any]] = None, auth_rotation=True) Dict[str, Any] [source]ο
Make a request against the Personio API, expecting a json response. Returns the parsed json response as dictionary. Will raise a PersonioApiError if the request fails.
- Parameters
path β the URL path for this request (relative to the Personio API base URL)
method β the HTTP request method (default: GET)
params β dictionary of URL parameters (optional)
data β dictionary of data to send in the request body (optional)
auth_rotation β set to True, if authentication keys should be rotated during this request (default: True for json requests)
- Returns
the parsed json response, when the request was successful, or a PersonioApiError
- request_paginated(path: str, method='GET', params: Optional[Dict[str, Any]] = None, data: Optional[Dict[str, Any]] = None, auth_rotation=True, limit=200) Dict[str, Any] [source]ο
Make a request against the Personio API, expecting a json response that may be paginated, i.e. not all results might have been returned after the first request. Will continue to make requests until no more results are provided by the Personio API. Returns the parsed json response as dictionary. Will raise a PersonioApiError if the request fails.
- Parameters
path β the URL path for this request (relative to the Personio API base URL)
method β the HTTP request method (default: GET)
params β dictionary of URL parameters (optional)
data β dictionary of data to send in the request body (optional)
auth_rotation β set to True, if authentication keys should be rotated during this request (default: True for json requests)
limit β the max. number of items to return in response to a single request. A higher limit means fewer requests will be made (though there is an upper bound that is enforced on the server side)
- Returns
the parsed json response, when the request was successful, or a PersonioApiError
- search(query: str, active_only=True) List[personio_py.models.Employee] [source]ο
Execute a search on the search index.
If the index does not exist, or has been invalidated or is expired, the full list of employees will be requested from the API.
During the search we perform a case insensitive match on the keywords in the search index. All tokens of the query will be matched individually. Tokens are separated by whitespace. A full match (i.e. all tokens match the keywords in order) is preferred over a partial match (only one or more tokens match).
For more details, please refer to
SearchIndex
.- Parameters
query β the query string
active_only β exclude inactive employees from the results (default: yes)
- Returns
the list of employees that matches the search query
- search_first(query: str, active_only=True) Optional[personio_py.models.Employee] [source]ο
Execute a search on the search index and return the first result (if there is one) or None.
This is basically the βIβm Feeling Luckyβ button. For details about the search function, please refer to
SearchIndex
.- Parameters
query β the query string
active_only β exclude inactive employees from the results (default: yes)
- Returns
the first search result or None, if there were no results
- update_attendance(attendance: personio_py.models.Attendance)[source]ο
Update an existing attendance record
Either an attendance id or o remote query is required. Remote queries are only executed if required. An Attendance object returned by get_attendances() include the attendance id. DO NOT SET THE ID YOURSELF.
- Parameters
attendance β The Attendance object holding the new data.
- Raises
ValueError: If a query is required but not allowed or the query does not provide exactly one result.
- update_employee(employee: personio_py.models.Employee)[source]ο
placeholder; not ready to be used
- update_project(project: personio_py.models.Project) personio_py.models.Project [source]ο
Updates a project record on the Personio servers.
- Parameters
project β The project object to be updated
- Raises
PersonioErrror β If the project could not be created on the Personio servers
modelsο
Definition of ORMs for objects that are available in the Personio API
- class personio_py.models.Absence(client: Personio = None, dynamic: Dict[str, Any] = None, dynamic_raw: List[DynamicAttr] = None, id_: int = None, status: str = None, comment: str = None, start_date: datetime.datetime = None, end_date: datetime.datetime = None, days_count: float = None, half_day_start: bool = False, half_day_end: bool = False, time_off_type: personio_py.models.AbsenceType = None, employee: personio_py.models.ShortEmployee = None, created_by: str = None, certificate: personio_py.models.Certificate = None, created_at: datetime.datetime = None, updated_at: datetime.datetime = None, **kwargs)[source]ο
- class personio_py.models.AbsenceEntitlement(id_: Optional[int] = None, name: Optional[str] = None, entitlement: Optional[float] = None, **kwargs)[source]ο
- class personio_py.models.AbsenceType(id_: Optional[int] = None, name: Optional[str] = None, category: Optional[str] = None, **kwargs)[source]ο
Bases:
personio_py.models.PersonioResource
- to_dict(nested=False) Dict[str, Any] [source]ο
Convert this PersonioResource to a dictionary that has the same structure as the json data from the Personio API.
- Parameters
nested β indicate that this resource is part of a nested dictionary (Personio resources can have a different serialization when they are part of a nested dictionaryβ¦)
- Returns
the Personio resource as dictionary (same structure as in the Personio API)
- class personio_py.models.Attendance(client: Personio = None, dynamic: Dict[str, Any] = None, dynamic_raw: List[DynamicAttr] = None, id_: int = None, employee_id: int = None, date: datetime.datetime = None, start_time: str = None, end_time: str = None, break_duration: int = None, comment: str = None, is_holiday: bool = None, is_on_time_off: bool = None, **kwargs)[source]ο
Bases:
personio_py.models.WritablePersonioResource
- to_body_params(patch_existing_attendance=False)[source]ο
Return the Attendance object in the representation expected by the Personio API
For an attendance record to be created all_values_required needs to be True. For patch operations only the attendance id is required, but it is not included into the body params.
:param patch_existing_attendance Get patch body. If False a create body is returned.
- to_dict(nested=False) Dict[str, Any] [source]ο
Convert this PersonioResource to a dictionary that has the same structure as the json data from the Personio API.
- Parameters
nested β indicate that this resource is part of a nested dictionary (Personio resources can have a different serialization when they are part of a nested dictionaryβ¦)
- Returns
the Personio resource as dictionary (same structure as in the Personio API)
- class personio_py.models.CostCenter(id_: Optional[int] = None, name: Optional[str] = None, percentage: Optional[float] = None, **kwargs)[source]ο
- class personio_py.models.Department(id_: Optional[int] = None, name: Optional[str] = None, **kwargs)[source]ο
- class personio_py.models.DynamicAttr(field_id, label, value)[source]ο
Bases:
NamedTuple
- field_id: intο
Alias for field number 0
- classmethod from_attributes(d: Dict[str, Dict[str, Any]]) List[personio_py.models.DynamicAttr] [source]ο
- classmethod from_dict(key: str, d: Dict[str, Any]) personio_py.models.DynamicAttr [source]ο
- label: strο
Alias for field number 1
- classmethod to_attributes(dyn_attrs: List[personio_py.models.DynamicAttr]) Dict[str, Dict[str, Any]] [source]ο
- value: Anyο
Alias for field number 2
- class personio_py.models.Employee(client: Personio = None, dynamic: Dict[str, Any] = None, dynamic_raw: List[DynamicAttr] = None, id_: int = None, first_name: str = None, last_name: str = None, email: str = None, gender: str = None, status: str = None, position: str = None, supervisor: personio_py.models.ShortEmployee = None, employment_type: str = None, weekly_working_hours: str = None, hire_date: datetime.datetime = None, contract_end_date: datetime.datetime = None, termination_date: datetime.datetime = None, termination_type: str = None, termination_reason: str = None, probation_period_end: datetime.datetime = None, created_at: datetime.datetime = None, last_modified_at: datetime.datetime = None, subcompany: str = None, office: personio_py.models.Office = None, department: personio_py.models.Department = None, cost_centers: List[personio_py.models.CostCenter] = None, holiday_calendar: personio_py.models.HolidayCalendar = None, absence_entitlement: List[personio_py.models.AbsenceEntitlement] = None, work_schedule: personio_py.models.WorkSchedule = None, fix_salary: float = None, fix_salary_interval: str = None, hourly_salary: float = None, vacation_day_balance: float = None, last_working_day: datetime.datetime = None, profile_picture: str = None, team: personio_py.models.Team = None, **kwargs)[source]ο
Bases:
personio_py.models.WritablePersonioResource
,personio_py.models.LabeledAttributesMixin
- class personio_py.models.HolidayCalendar(id_: Optional[int] = None, name: Optional[str] = None, country: Optional[str] = None, state: Optional[str] = None, **kwargs)[source]ο
- class personio_py.models.LabeledAttributesMixin(*args, **kwargs)[source]ο
Bases:
personio_py.models.PersonioResource
Personio Resources that use the
LabeledAttributesMixin
expect data in a different format than the regular key-value pattern. Example:"first_name": { "label": "First name", "value": "Richard" }
Instead of
"first_name": "Richard"
we get a dictionary where the label of the field and its value are attributes of another dictionary.This format is currently used by
Employee
andShortEmployee
and was probably chosen because Personio allows to specify custom fields for employees with custom label names.- to_dict(nested=False) Dict[str, Any] [source]ο
Convert this PersonioResource to a dictionary that has the same structure as the json data from the Personio API.
- Parameters
nested β indicate that this resource is part of a nested dictionary (Personio resources can have a different serialization when they are part of a nested dictionaryβ¦)
- Returns
the Personio resource as dictionary (same structure as in the Personio API)
- class personio_py.models.Office(id_: Optional[int] = None, name: Optional[str] = None, **kwargs)[source]ο
- class personio_py.models.PersonioResource(client: Personio = None, **kwargs)[source]ο
Bases:
object
- classmethod from_dict(d: Dict[str, Any], client: Personio = None) __class__ [source]ο
Create an instance of this PersonioResource from the specified dictionary data, which is parsed version of the json data from the Personio API.
- Parameters
d β create an instance from this data
client β the Personio API client (optional). Used to provide additional operations on this resource, when available (e.g. request more data or write changes back to Personio)
- Returns
a new instance of this class based on the provided data
- to_dict(nested=False) Dict[str, Any] [source]ο
Convert this PersonioResource to a dictionary that has the same structure as the json data from the Personio API.
- Parameters
nested β indicate that this resource is part of a nested dictionary (Personio resources can have a different serialization when they are part of a nested dictionaryβ¦)
- Returns
the Personio resource as dictionary (same structure as in the Personio API)
- class personio_py.models.Project(client: Personio = None, dynamic: Dict[str, Any] = None, dynamic_raw: List[DynamicAttr] = None, id_: int = None, name: str = None, active: bool = None, created_at: datetime.datetime = None, updated_at: datetime.datetime = None, **kwargs)[source]ο
Bases:
personio_py.models.WritablePersonioResource
- to_dict(nested=False) Dict[str, Any] [source]ο
Convert this PersonioResource to a dictionary that has the same structure as the json data from the Personio API.
- Parameters
nested β indicate that this resource is part of a nested dictionary (Personio resources can have a different serialization when they are part of a nested dictionaryβ¦)
- Returns
the Personio resource as dictionary (same structure as in the Personio API)
- class personio_py.models.ShortEmployee(client: Personio = None, id_: int = None, first_name: str = None, last_name: str = None, email: str = None, **kwargs)[source]ο
- class personio_py.models.Team(id_: Optional[int] = None, name: Optional[str] = None, **kwargs)[source]ο
- class personio_py.models.WorkSchedule(id_: Optional[int] = None, name: Optional[str] = None, valid_from: Optional[datetime.datetime] = None, monday: Optional[datetime.timedelta] = None, tuesday: Optional[datetime.timedelta] = None, wednesday: Optional[datetime.timedelta] = None, thursday: Optional[datetime.timedelta] = None, friday: Optional[datetime.timedelta] = None, saturday: Optional[datetime.timedelta] = None, sunday: Optional[datetime.timedelta] = None, **kwargs)[source]ο
- class personio_py.models.WritablePersonioResource(client: Personio = None, dynamic: List[DynamicAttr] = None, dynamic_fields: List[personio_py.mapping.DynamicMapping] = None, **kwargs)[source]ο
Bases:
personio_py.models.PersonioResource
- classmethod from_dict(d: Dict[str, Any], client: Personio = None, dynamic_fields: List[personio_py.mapping.DynamicMapping] = None) __class__ [source]ο
Create an instance of this PersonioResource from the specified dictionary data, which is parsed version of the json data from the Personio API.
- Parameters
d β create an instance from this data
client β the Personio API client (optional). Used to provide additional operations on this resource, when available (e.g. request more data or write changes back to Personio)
- Returns
a new instance of this class based on the provided data
- to_dict(nested=False) Dict[str, Any] [source]ο
Convert this PersonioResource to a dictionary that has the same structure as the json data from the Personio API.
- Parameters
nested β indicate that this resource is part of a nested dictionary (Personio resources can have a different serialization when they are part of a nested dictionaryβ¦)
- Returns
the Personio resource as dictionary (same structure as in the Personio API)
- personio_py.models.get_client(resource: personio_py.models.PersonioResource, client: Personio = None)[source]ο
mappingο
mappings from Personio API fields to Python data types and vice versa are defined in this module
- class personio_py.mapping.BooleanFieldMapping(api_field: str, class_field: str)[source]ο
- class personio_py.mapping.DateFieldMapping(api_field: str, class_field: str)[source]ο
- class personio_py.mapping.DateTimeFieldMapping(api_field: str, class_field: str)[source]ο
- class personio_py.mapping.DurationFieldMapping(api_field: str, class_field: str)[source]ο
Bases:
personio_py.mapping.FieldMapping
- deserialize(value: str, **kwargs) datetime.timedelta [source]ο
Deserialize the Personio API value to a more useful Python data type.
- Parameters
value β the value as provided by the Personio API
kwargs β additional parameters (to be used by subclasses)
- Returns
the deserialized value
- pattern = re.compile('\\d\\d?:\\d\\d')ο
- class personio_py.mapping.DynamicMapping(field_id: int, alias: str, data_type: Type[personio_py.mapping.T])[source]ο
Bases:
NamedTuple
Defines a mapping from a dynamic field to a more memorable name and its actual data type, so that it may be converted into a proper python type, if possible.
- alias: strο
a more memorable name than the field_id, will be used as dictionary key
- data_type: Type[personio_py.mapping.T]ο
the data type of the field, for automatic conversion (e.g. str to datetime)
- field_id: intο
the id number of the dynamic field, e.g. for βdynamic_123456β, field_id=123456
- class personio_py.mapping.FieldMapping(api_field: str, class_field: str, field_type: Type[personio_py.mapping.T])[source]ο
Bases:
object
A generic mapping from a Personio API field to a Python object. The default implementation works great for strings, but for more complex types, please refer to the subclasses of
FieldMapping
.- Parameters
api_field β name of the field in the Personio API
class_field β name of the attribute in the target Python object
field_type β data type of the field
- deserialize(value: Union[str, Dict], **kwargs) personio_py.mapping.T [source]ο
Deserialize the Personio API value to a more useful Python data type.
- Parameters
value β the value as provided by the Personio API
kwargs β additional parameters (to be used by subclasses)
- Returns
the deserialized value
- class personio_py.mapping.ListFieldMapping(item_mapping: personio_py.mapping.FieldMapping)[source]ο
Bases:
personio_py.mapping.FieldMapping
- deserialize(values: List[Any], client: Personio = None) List[Any] [source]ο
Deserialize the Personio API value to a more useful Python data type.
- Parameters
value β the value as provided by the Personio API
kwargs β additional parameters (to be used by subclasses)
- Returns
the deserialized value
- class personio_py.mapping.MultiTagFieldMapping(api_field: str, class_field: str)[source]ο
- class personio_py.mapping.NumericFieldMapping(api_field: str, class_field: str, field_type=<class 'float'>)[source]ο
Bases:
personio_py.mapping.FieldMapping
- deserialize(value: Union[int, float, str], **kwargs) Union[int, float, str] [source]ο
Deserialize the Personio API value to a more useful Python data type.
- Parameters
value β the value as provided by the Personio API
kwargs β additional parameters (to be used by subclasses)
- Returns
the deserialized value
- class personio_py.mapping.ObjectFieldMapping(api_field: str, class_field: str, field_type: Type[PersonioResourceType])[source]ο
Bases:
personio_py.mapping.FieldMapping
- deserialize(value: Dict, client: Personio = None) Optional[PersonioResourceType] [source]ο
Deserialize the Personio API value to a more useful Python data type.
- Parameters
value β the value as provided by the Personio API
kwargs β additional parameters (to be used by subclasses)
- Returns
the deserialized value
Errorsο
- exception personio_py.PersonioError[source]ο
Bases:
Exception
A generic error caused by personio-py
- exception personio_py.MissingCredentialsError[source]ο
Bases:
personio_py.errors.PersonioError
you are missing some strictly required credentials
- exception personio_py.PersonioApiError(status_code: int, message: str, error_code: Optional[int] = None, errors: Optional[Dict[str, Any]] = None, response: Optional[requests.models.Response] = None)[source]ο
Bases:
personio_py.errors.PersonioError
An error response from the Personio HTTP API
- Parameters
status_code β the HTTP status code of the API response
message β the error message from the Personio API
error_code β the error code that is provided with the Personio API response (not the HTTP status code!)
errors β details about the errors that are provided by the Personio API as dictionary
response β the HTTP response
- exception personio_py.UnsupportedMethodError(method: str, clazz: Type)[source]ο
Bases:
personio_py.errors.PersonioError
this method is not supported by this class (but it might be by a similar one)