Skip to content

API design guidelines

Marcos edited this page Jun 13, 2022 · 12 revisions

Be "Pythonic"

  • Use Zen of Python as guiding principles
  • Follow Python coding conventions
  • Use classes to represent each API session
  • Expose API properties as properties of Session
    • Or of Repeated Capabilities object: session.channels[0].my_property
  • Use context managers where appropriate:
    • Session lifetime
    • Acquisition / Generation control
    • Session locking (synchronization)
  • Use Exceptions for errors
  • Use warnings package for warnings
  • Handle memory allocation for the user
  • Return values rather than modify references
  • Use native Python datatypes as much as possible

Close 1-to-1 mapping of the C APIs

  • Method names will be derived from C function names
  • Enums:
    • Names will be derived from CVI FP names (C API has no enums)
    • Values will be derived from C constant names

Deviate from C APIs in the following ways

  • No support for external calibration
  • No support for IVI-specific functions that don't apply to Python (class driver-only, interchangeability stuff, etc.)
  • Don't include obsolete functions / properties
    • Anything obsoleted after 1.0 release will need to be handled differently
    • Anything that's there but can only be used by devices that use "Traditional DAQ" driver
  • Don't include functions that are simple wrappers around setting/getting an attribute
    • Redundancy means confusion
    • Clients don't get type safety benefit that you get in C function anyway
  • If there's debate on whether a function should be included or not, then don't include it
    • Trivial to add if needed
    • Hard to remove once added
  • Create "fancy" functions on a case-by-case basis

Enums

  • Don't use enums as constants
    • LabVIEW APIs provide constants as ring controls or enums. Don't do that for Python
  • Don't use enums when a bool will do
    • C uses ViBoolean but provides named constants. Don't do that for Python
    • LabVIEW APIs provide ring controls or enums. Don't do that for Python
    • We think this is more obvious for Python clients
    • If and when a third choice is introduced, we can switch to an enum while maintaining source compatibility thanks to the dynamic nature of Python.

Default values for methods

  • For the most part, defaults are a subset of the defaults in the corresponding LabVIEW VIs
    • Less defaults than in LabVIEW APIs, where we may have overdone it
    • Defaults can always be added later
    • But never removed
    • Deviate from LabVIEW default values only in very specific cases (i.e. open session with reset=False)

Encourage use of cross-device trigger routing

  • Making the method parameters / attributes for terminals simple str, rather than an enum of pre-defined strings.
    • For example: `session_to_dev1.digital_edge_measure_trigger_input_terminal = '/Dev2/SourceCompleteEvent'
    • This trades off discoverability for power and ease-of-use in large triggering applications.
      • In LabVIEW APIs such as NI-DCPower and DAQmx, I/O controls are auto-populated with actual available terminals.
      • In LabVIEW APIs such as NI-FGEN and NI-SCOPE, enums obscure this powerful feature.
    • In future revisions of our Python APIs, a better solution (programmatically generated docstring and/or enums?) can be devised.
  • We went this route (pun intended) because NI's routing subsystem will:
    • Find and reserve free routing resources such as PXI trigger lines for you.
    • Reuse routing resources when appropriate.
    • Program intermediate hardware such as bridges between segments on 18-slot PXI chassis.
  • Sadly this doesn't apply to NI-DMM and NI-SWITCH because these drivers have not been updated to support NI's routing subsystem.

Provide high-performance alternative to Python lists for waveforms

  • Python lists are slow and inefficient for large data sets
  • Use numpy.array and array.array
  • "Advanced" API for users who need it or care about it

Disclaimer

We acknowledge that we could go further and provide more OOP abstractions similar to .NET APIs. For example, we could provide classes for NI-FGEN's frequency lists, scripts, etc.

We could also have further improved the signatures of some functions had we designed these Python APIs by hand and from scratch.

However, in order to keep things simple, reduce the scope of the project, and make code-generation viable, we have decided against it. Feel free to open an issue if you disagree.