Table of Contents
Fetching ...

Adaptoring: Adapter Generation to Provide an Alternative API for a Library

Lars Reimann, Günter Kniesel-Wünsche

TL;DR

Adaptoring introduces a wrapper-based approach to provide an alternative API for existing libraries without modifying the original code, preserving compatibility while improving learnability and usability. It formalizes adaptorable API transformations, automates adapter generation for Python, and combines usage data with docstring-driven inferences to suggest deletions, parameter optionality, and preconditions. A GUI-assisted editing workflow enables human review and extension of automated suggestions, and an evolution framework addresses maintaining adapters as the original library evolves. Empirical results on scikit-learn and other libraries demonstrate meaningful API size reductions, competitive inference precision, and efficient end-to-end adapter generation times, indicating practical viability for large-scale libraries.

Abstract

Third-party libraries are a cornerstone of fast application development. To enable efficient use, libraries must provide a well-designed API. An obscure API instead slows down the learning process and can lead to erroneous use. The usual approach to improve the API of a library is to edit its code directly, either keeping the old API but deprecating it (temporarily increasing the API size) or dropping it (introducing breaking changes). If maintainers are unwilling to make such changes, others need to create a hard fork, which they can refactor. But then it is difficult to incorporate changes to the original library, such as bug fixes or performance improvements. In this paper, we instead explore the use of the adapter pattern to provide a new API as a new library that calls the original library internally. This allows the new library to leverage all implementation changes to the original library, at no additional cost. We call this approach adaptoring. To make the approach practical, we identify API transformations for which adapter code can be generated automatically, and investigate which transformations can be inferred automatically, based on the documentation and usage patterns of the original library. For cases where automated inference is not possible, we present a tool that lets developers manually specify API transformations. Finally, we consider the issue of migrating the generated adapters if the original library introduces breaking changes. We implemented our approach for Python, demonstrating its effectiveness to quickly provide an alternative API even for large libraries.

Adaptoring: Adapter Generation to Provide an Alternative API for a Library

TL;DR

Adaptoring introduces a wrapper-based approach to provide an alternative API for existing libraries without modifying the original code, preserving compatibility while improving learnability and usability. It formalizes adaptorable API transformations, automates adapter generation for Python, and combines usage data with docstring-driven inferences to suggest deletions, parameter optionality, and preconditions. A GUI-assisted editing workflow enables human review and extension of automated suggestions, and an evolution framework addresses maintaining adapters as the original library evolves. Empirical results on scikit-learn and other libraries demonstrate meaningful API size reductions, competitive inference precision, and efficient end-to-end adapter generation times, indicating practical viability for large-scale libraries.

Abstract

Third-party libraries are a cornerstone of fast application development. To enable efficient use, libraries must provide a well-designed API. An obscure API instead slows down the learning process and can lead to erroneous use. The usual approach to improve the API of a library is to edit its code directly, either keeping the old API but deprecating it (temporarily increasing the API size) or dropping it (introducing breaking changes). If maintainers are unwilling to make such changes, others need to create a hard fork, which they can refactor. But then it is difficult to incorporate changes to the original library, such as bug fixes or performance improvements. In this paper, we instead explore the use of the adapter pattern to provide a new API as a new library that calls the original library internally. This allows the new library to leverage all implementation changes to the original library, at no additional cost. We call this approach adaptoring. To make the approach practical, we identify API transformations for which adapter code can be generated automatically, and investigate which transformations can be inferred automatically, based on the documentation and usage patterns of the original library. For cases where automated inference is not possible, we present a tool that lets developers manually specify API transformations. Finally, we consider the issue of migrating the generated adapters if the original library introduces breaking changes. We implemented our approach for Python, demonstrating its effectiveness to quickly provide an alternative API even for large libraries.
Paper Structure (19 sections, 1 equation, 4 figures, 3 tables)

This paper contains 19 sections, 1 equation, 4 figures, 3 tables.

Figures (4)

  • Figure 1: Discussed approaches for creating a new API for a library. Blue boxes represent the original library and green boxes the new one. Original users ( ) interact with the original function, unless they migrate their own code, while new users ( ) directly use the changed function. Adaptoring builds a new API based on the unmodified, original one.
  • Figure 2: Example of preconditions hidden in a documentation snippet in Numpydoc format.
  • Figure 3: Excerpt of the GUI of the API Editor. Users can browse through the navigator on the left-hand-side, filter the displayed elements via the input field at the bottom, inspect the annotations of the selected element, and edit annotations. The image shows a selected function and its first parameter.
  • Figure 4: Number of useful, useless and unused elements in the public API of scikit-learn. For classes and functions, all used elements are useful.