github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/librclone/python/rclone.py (about)

     1  """
     2  Python interface to librclone.so using ctypes
     3  
     4  Create an rclone object
     5  
     6      rclone = Rclone(shared_object="/path/to/librclone.so")
     7  
     8  Then call rpc calls on it
     9  
    10      rclone.rpc("rc/noop", a=42, b="string", c=[1234])
    11  
    12  When finished, close it
    13  
    14      rclone.close()
    15  """
    16  
    17  __all__ = ('Rclone', 'RcloneException')
    18  
    19  import os
    20  import json
    21  import subprocess
    22  from ctypes import *
    23  
    24  class RcloneRPCString(c_char_p):
    25      """
    26      This is a raw string from the C API
    27  
    28      With a plain c_char_p type, ctypes will replace it with a
    29      regular Python string object that cannot be used with
    30      RcloneFreeString. Subclassing prevents it, while the string
    31      can still be retrieved from attribute value.
    32      """
    33      pass
    34  
    35  class RcloneRPCResult(Structure):
    36      """
    37      This is returned from the C API when calling RcloneRPC
    38      """
    39      _fields_ = [("Output", RcloneRPCString),
    40                  ("Status", c_int)]
    41  
    42  class RcloneException(Exception):
    43      """
    44      Exception raised from rclone
    45  
    46      This will have the attributes:
    47  
    48      output - a dictionary from the call
    49      status - a status number
    50      """
    51      def __init__(self, output, status):
    52          self.output = output
    53          self.status = status
    54          message = self.output.get('error', 'Unknown rclone error')
    55          super().__init__(message)
    56  
    57  class Rclone():
    58      """
    59      Interface to Rclone via librclone.so
    60  
    61      Initialise with shared_object as the file path of librclone.so
    62      """
    63      def __init__(self, shared_object=f"./librclone{'.dll' if os.name == 'nt' else '.so'}"):
    64          self.rclone = CDLL(shared_object)
    65          self.rclone.RcloneRPC.restype = RcloneRPCResult
    66          self.rclone.RcloneRPC.argtypes = (c_char_p, c_char_p)
    67          self.rclone.RcloneFreeString.restype = None
    68          self.rclone.RcloneFreeString.argtypes = (c_char_p,)
    69          self.rclone.RcloneInitialize.restype = None
    70          self.rclone.RcloneInitialize.argtypes = ()
    71          self.rclone.RcloneFinalize.restype = None
    72          self.rclone.RcloneFinalize.argtypes = ()
    73          self.rclone.RcloneInitialize()
    74      def rpc(self, method, **kwargs):
    75          """
    76          Call an rclone RC API call with the kwargs given.
    77  
    78          The result will be a dictionary.
    79  
    80          If an exception is raised from rclone it will of type
    81          RcloneException.
    82          """
    83          method = method.encode("utf-8")
    84          parameters = json.dumps(kwargs).encode("utf-8")
    85          resp = self.rclone.RcloneRPC(method, parameters)
    86          output = json.loads(resp.Output.value.decode("utf-8"))
    87          self.rclone.RcloneFreeString(resp.Output)
    88          status = resp.Status
    89          if status != 200:
    90              raise RcloneException(output, status)
    91          return output
    92      def close(self):
    93          """
    94          Call to finish with the rclone connection
    95          """
    96          self.rclone.RcloneFinalize()
    97          self.rclone = None
    98      @classmethod
    99      def build(cls, shared_object):
   100          """
   101          Builds rclone to shared_object if it doesn't already exist
   102  
   103          Requires go to be installed
   104          """
   105          if os.path.exists(shared_object):
   106              return
   107          print("Building "+shared_object)
   108          subprocess.check_call(["go", "build", "--buildmode=c-shared", "-o", shared_object, "github.com/rclone/rclone/librclone"])