1""" 2libpq access using ctypes 3""" 4 5# Copyright (C) 2020-2021 The Psycopg Team 6 7import ctypes 8import ctypes.util 9import sys 10from ctypes import Structure, CFUNCTYPE, POINTER 11from ctypes import c_char, c_char_p, c_int, c_size_t, c_ubyte, c_uint, c_void_p 12from typing import List, Optional, Tuple 13 14from ..errors import NotSupportedError 15 16if sys.platform == "win32": 17 libname = ctypes.util.find_library("libpq.dll") 18elif sys.platform == "darwin": 19 libname = ctypes.util.find_library("libpq.dylib") 20else: 21 libname = ctypes.util.find_library("pq") 22if not libname: 23 raise ImportError("libpq library not found") 24 25pq = ctypes.cdll.LoadLibrary(libname) 26 27# Get the libpq version to define what functions are available. 28 29PQlibVersion = pq.PQlibVersion 30PQlibVersion.argtypes = [] 31PQlibVersion.restype = c_int 32 33libpq_version = PQlibVersion() 34 35 36# libpq data types 37 38 39Oid = c_uint 40 41 42class PGconn_struct(Structure): 43 _fields_: List[Tuple[str, type]] = [] 44 45 46class PGresult_struct(Structure): 47 _fields_: List[Tuple[str, type]] = [] 48 49 50class PQconninfoOption_struct(Structure): 51 _fields_ = [ 52 ("keyword", c_char_p), 53 ("envvar", c_char_p), 54 ("compiled", c_char_p), 55 ("val", c_char_p), 56 ("label", c_char_p), 57 ("dispchar", c_char_p), 58 ("dispsize", c_int), 59 ] 60 61 62class PGnotify_struct(Structure): 63 _fields_ = [ 64 ("relname", c_char_p), 65 ("be_pid", c_int), 66 ("extra", c_char_p), 67 ] 68 69 70class PGcancel_struct(Structure): 71 _fields_: List[Tuple[str, type]] = [] 72 73 74class PGresAttDesc_struct(Structure): 75 _fields_ = [ 76 ("name", c_char_p), 77 ("tableid", Oid), 78 ("columnid", c_int), 79 ("format", c_int), 80 ("typid", Oid), 81 ("typlen", c_int), 82 ("atttypmod", c_int), 83 ] 84 85 86PGconn_ptr = POINTER(PGconn_struct) 87PGresult_ptr = POINTER(PGresult_struct) 88PQconninfoOption_ptr = POINTER(PQconninfoOption_struct) 89PGnotify_ptr = POINTER(PGnotify_struct) 90PGcancel_ptr = POINTER(PGcancel_struct) 91PGresAttDesc_ptr = POINTER(PGresAttDesc_struct) 92 93 94# Function definitions as explained in PostgreSQL 12 documentation 95 96# 33.1. Database Connection Control Functions 97 98# PQconnectdbParams: doesn't seem useful, won't wrap for now 99 100PQconnectdb = pq.PQconnectdb 101PQconnectdb.argtypes = [c_char_p] 102PQconnectdb.restype = PGconn_ptr 103 104# PQsetdbLogin: not useful 105# PQsetdb: not useful 106 107# PQconnectStartParams: not useful 108 109PQconnectStart = pq.PQconnectStart 110PQconnectStart.argtypes = [c_char_p] 111PQconnectStart.restype = PGconn_ptr 112 113PQconnectPoll = pq.PQconnectPoll 114PQconnectPoll.argtypes = [PGconn_ptr] 115PQconnectPoll.restype = c_int 116 117PQconndefaults = pq.PQconndefaults 118PQconndefaults.argtypes = [] 119PQconndefaults.restype = PQconninfoOption_ptr 120 121PQconninfoFree = pq.PQconninfoFree 122PQconninfoFree.argtypes = [PQconninfoOption_ptr] 123PQconninfoFree.restype = None 124 125PQconninfo = pq.PQconninfo 126PQconninfo.argtypes = [PGconn_ptr] 127PQconninfo.restype = PQconninfoOption_ptr 128 129PQconninfoParse = pq.PQconninfoParse 130PQconninfoParse.argtypes = [c_char_p, POINTER(c_char_p)] 131PQconninfoParse.restype = PQconninfoOption_ptr 132 133PQfinish = pq.PQfinish 134PQfinish.argtypes = [PGconn_ptr] 135PQfinish.restype = None 136 137PQreset = pq.PQreset 138PQreset.argtypes = [PGconn_ptr] 139PQreset.restype = None 140 141PQresetStart = pq.PQresetStart 142PQresetStart.argtypes = [PGconn_ptr] 143PQresetStart.restype = c_int 144 145PQresetPoll = pq.PQresetPoll 146PQresetPoll.argtypes = [PGconn_ptr] 147PQresetPoll.restype = c_int 148 149PQping = pq.PQping 150PQping.argtypes = [c_char_p] 151PQping.restype = c_int 152 153 154# 33.2. Connection Status Functions 155 156PQdb = pq.PQdb 157PQdb.argtypes = [PGconn_ptr] 158PQdb.restype = c_char_p 159 160PQuser = pq.PQuser 161PQuser.argtypes = [PGconn_ptr] 162PQuser.restype = c_char_p 163 164PQpass = pq.PQpass 165PQpass.argtypes = [PGconn_ptr] 166PQpass.restype = c_char_p 167 168PQhost = pq.PQhost 169PQhost.argtypes = [PGconn_ptr] 170PQhost.restype = c_char_p 171 172_PQhostaddr = None 173 174if libpq_version >= 120000: 175 _PQhostaddr = pq.PQhostaddr 176 _PQhostaddr.argtypes = [PGconn_ptr] 177 _PQhostaddr.restype = c_char_p 178 179 180def PQhostaddr(pgconn: PGconn_struct) -> bytes: 181 if not _PQhostaddr: 182 raise NotSupportedError( 183 f"PQhostaddr requires libpq from PostgreSQL 12," 184 f" {libpq_version} available instead" 185 ) 186 187 return _PQhostaddr(pgconn) 188 189 190PQport = pq.PQport 191PQport.argtypes = [PGconn_ptr] 192PQport.restype = c_char_p 193 194PQtty = pq.PQtty 195PQtty.argtypes = [PGconn_ptr] 196PQtty.restype = c_char_p 197 198PQoptions = pq.PQoptions 199PQoptions.argtypes = [PGconn_ptr] 200PQoptions.restype = c_char_p 201 202PQstatus = pq.PQstatus 203PQstatus.argtypes = [PGconn_ptr] 204PQstatus.restype = c_int 205 206PQtransactionStatus = pq.PQtransactionStatus 207PQtransactionStatus.argtypes = [PGconn_ptr] 208PQtransactionStatus.restype = c_int 209 210PQparameterStatus = pq.PQparameterStatus 211PQparameterStatus.argtypes = [PGconn_ptr, c_char_p] 212PQparameterStatus.restype = c_char_p 213 214PQprotocolVersion = pq.PQprotocolVersion 215PQprotocolVersion.argtypes = [PGconn_ptr] 216PQprotocolVersion.restype = c_int 217 218PQserverVersion = pq.PQserverVersion 219PQserverVersion.argtypes = [PGconn_ptr] 220PQserverVersion.restype = c_int 221 222PQerrorMessage = pq.PQerrorMessage 223PQerrorMessage.argtypes = [PGconn_ptr] 224PQerrorMessage.restype = c_char_p 225 226PQsocket = pq.PQsocket 227PQsocket.argtypes = [PGconn_ptr] 228PQsocket.restype = c_int 229 230PQbackendPID = pq.PQbackendPID 231PQbackendPID.argtypes = [PGconn_ptr] 232PQbackendPID.restype = c_int 233 234PQconnectionNeedsPassword = pq.PQconnectionNeedsPassword 235PQconnectionNeedsPassword.argtypes = [PGconn_ptr] 236PQconnectionNeedsPassword.restype = c_int 237 238PQconnectionUsedPassword = pq.PQconnectionUsedPassword 239PQconnectionUsedPassword.argtypes = [PGconn_ptr] 240PQconnectionUsedPassword.restype = c_int 241 242PQsslInUse = pq.PQsslInUse 243PQsslInUse.argtypes = [PGconn_ptr] 244PQsslInUse.restype = c_int 245 246# TODO: PQsslAttribute, PQsslAttributeNames, PQsslStruct, PQgetssl 247 248 249# 33.3. Command Execution Functions 250 251PQexec = pq.PQexec 252PQexec.argtypes = [PGconn_ptr, c_char_p] 253PQexec.restype = PGresult_ptr 254 255PQexecParams = pq.PQexecParams 256PQexecParams.argtypes = [ 257 PGconn_ptr, 258 c_char_p, 259 c_int, 260 POINTER(Oid), 261 POINTER(c_char_p), 262 POINTER(c_int), 263 POINTER(c_int), 264 c_int, 265] 266PQexecParams.restype = PGresult_ptr 267 268PQprepare = pq.PQprepare 269PQprepare.argtypes = [PGconn_ptr, c_char_p, c_char_p, c_int, POINTER(Oid)] 270PQprepare.restype = PGresult_ptr 271 272PQexecPrepared = pq.PQexecPrepared 273PQexecPrepared.argtypes = [ 274 PGconn_ptr, 275 c_char_p, 276 c_int, 277 POINTER(c_char_p), 278 POINTER(c_int), 279 POINTER(c_int), 280 c_int, 281] 282PQexecPrepared.restype = PGresult_ptr 283 284PQdescribePrepared = pq.PQdescribePrepared 285PQdescribePrepared.argtypes = [PGconn_ptr, c_char_p] 286PQdescribePrepared.restype = PGresult_ptr 287 288PQdescribePortal = pq.PQdescribePortal 289PQdescribePortal.argtypes = [PGconn_ptr, c_char_p] 290PQdescribePortal.restype = PGresult_ptr 291 292PQresultStatus = pq.PQresultStatus 293PQresultStatus.argtypes = [PGresult_ptr] 294PQresultStatus.restype = c_int 295 296# PQresStatus: not needed, we have pretty enums 297 298PQresultErrorMessage = pq.PQresultErrorMessage 299PQresultErrorMessage.argtypes = [PGresult_ptr] 300PQresultErrorMessage.restype = c_char_p 301 302# TODO: PQresultVerboseErrorMessage 303 304PQresultErrorField = pq.PQresultErrorField 305PQresultErrorField.argtypes = [PGresult_ptr, c_int] 306PQresultErrorField.restype = c_char_p 307 308PQclear = pq.PQclear 309PQclear.argtypes = [PGresult_ptr] 310PQclear.restype = None 311 312 313# 33.3.2. Retrieving Query Result Information 314 315PQntuples = pq.PQntuples 316PQntuples.argtypes = [PGresult_ptr] 317PQntuples.restype = c_int 318 319PQnfields = pq.PQnfields 320PQnfields.argtypes = [PGresult_ptr] 321PQnfields.restype = c_int 322 323PQfname = pq.PQfname 324PQfname.argtypes = [PGresult_ptr, c_int] 325PQfname.restype = c_char_p 326 327# PQfnumber: useless and hard to use 328 329PQftable = pq.PQftable 330PQftable.argtypes = [PGresult_ptr, c_int] 331PQftable.restype = Oid 332 333PQftablecol = pq.PQftablecol 334PQftablecol.argtypes = [PGresult_ptr, c_int] 335PQftablecol.restype = c_int 336 337PQfformat = pq.PQfformat 338PQfformat.argtypes = [PGresult_ptr, c_int] 339PQfformat.restype = c_int 340 341PQftype = pq.PQftype 342PQftype.argtypes = [PGresult_ptr, c_int] 343PQftype.restype = Oid 344 345PQfmod = pq.PQfmod 346PQfmod.argtypes = [PGresult_ptr, c_int] 347PQfmod.restype = c_int 348 349PQfsize = pq.PQfsize 350PQfsize.argtypes = [PGresult_ptr, c_int] 351PQfsize.restype = c_int 352 353PQbinaryTuples = pq.PQbinaryTuples 354PQbinaryTuples.argtypes = [PGresult_ptr] 355PQbinaryTuples.restype = c_int 356 357PQgetvalue = pq.PQgetvalue 358PQgetvalue.argtypes = [PGresult_ptr, c_int, c_int] 359PQgetvalue.restype = POINTER(c_char) # not a null-terminated string 360 361PQgetisnull = pq.PQgetisnull 362PQgetisnull.argtypes = [PGresult_ptr, c_int, c_int] 363PQgetisnull.restype = c_int 364 365PQgetlength = pq.PQgetlength 366PQgetlength.argtypes = [PGresult_ptr, c_int, c_int] 367PQgetlength.restype = c_int 368 369PQnparams = pq.PQnparams 370PQnparams.argtypes = [PGresult_ptr] 371PQnparams.restype = c_int 372 373PQparamtype = pq.PQparamtype 374PQparamtype.argtypes = [PGresult_ptr, c_int] 375PQparamtype.restype = Oid 376 377# PQprint: pretty useless 378 379# 33.3.3. Retrieving Other Result Information 380 381PQcmdStatus = pq.PQcmdStatus 382PQcmdStatus.argtypes = [PGresult_ptr] 383PQcmdStatus.restype = c_char_p 384 385PQcmdTuples = pq.PQcmdTuples 386PQcmdTuples.argtypes = [PGresult_ptr] 387PQcmdTuples.restype = c_char_p 388 389PQoidValue = pq.PQoidValue 390PQoidValue.argtypes = [PGresult_ptr] 391PQoidValue.restype = Oid 392 393 394# 33.3.4. Escaping Strings for Inclusion in SQL Commands 395 396PQescapeLiteral = pq.PQescapeLiteral 397PQescapeLiteral.argtypes = [PGconn_ptr, c_char_p, c_size_t] 398PQescapeLiteral.restype = POINTER(c_char) 399 400PQescapeIdentifier = pq.PQescapeIdentifier 401PQescapeIdentifier.argtypes = [PGconn_ptr, c_char_p, c_size_t] 402PQescapeIdentifier.restype = POINTER(c_char) 403 404PQescapeStringConn = pq.PQescapeStringConn 405# TODO: raises "wrong type" error 406# PQescapeStringConn.argtypes = [ 407# PGconn_ptr, c_char_p, c_char_p, c_size_t, POINTER(c_int) 408# ] 409PQescapeStringConn.restype = c_size_t 410 411PQescapeString = pq.PQescapeString 412# TODO: raises "wrong type" error 413# PQescapeString.argtypes = [c_char_p, c_char_p, c_size_t] 414PQescapeString.restype = c_size_t 415 416PQescapeByteaConn = pq.PQescapeByteaConn 417PQescapeByteaConn.argtypes = [ 418 PGconn_ptr, 419 POINTER(c_char), # actually POINTER(c_ubyte) but this is easier 420 c_size_t, 421 POINTER(c_size_t), 422] 423PQescapeByteaConn.restype = POINTER(c_ubyte) 424 425PQescapeBytea = pq.PQescapeBytea 426PQescapeBytea.argtypes = [ 427 POINTER(c_char), # actually POINTER(c_ubyte) but this is easier 428 c_size_t, 429 POINTER(c_size_t), 430] 431PQescapeBytea.restype = POINTER(c_ubyte) 432 433 434PQunescapeBytea = pq.PQunescapeBytea 435PQunescapeBytea.argtypes = [ 436 POINTER(c_char), # actually POINTER(c_ubyte) but this is easier 437 POINTER(c_size_t), 438] 439PQunescapeBytea.restype = POINTER(c_ubyte) 440 441 442# 33.4. Asynchronous Command Processing 443 444PQsendQuery = pq.PQsendQuery 445PQsendQuery.argtypes = [PGconn_ptr, c_char_p] 446PQsendQuery.restype = c_int 447 448PQsendQueryParams = pq.PQsendQueryParams 449PQsendQueryParams.argtypes = [ 450 PGconn_ptr, 451 c_char_p, 452 c_int, 453 POINTER(Oid), 454 POINTER(c_char_p), 455 POINTER(c_int), 456 POINTER(c_int), 457 c_int, 458] 459PQsendQueryParams.restype = c_int 460 461PQsendPrepare = pq.PQsendPrepare 462PQsendPrepare.argtypes = [PGconn_ptr, c_char_p, c_char_p, c_int, POINTER(Oid)] 463PQsendPrepare.restype = c_int 464 465PQsendQueryPrepared = pq.PQsendQueryPrepared 466PQsendQueryPrepared.argtypes = [ 467 PGconn_ptr, 468 c_char_p, 469 c_int, 470 POINTER(c_char_p), 471 POINTER(c_int), 472 POINTER(c_int), 473 c_int, 474] 475PQsendQueryPrepared.restype = c_int 476 477PQsendDescribePrepared = pq.PQsendDescribePrepared 478PQsendDescribePrepared.argtypes = [PGconn_ptr, c_char_p] 479PQsendDescribePrepared.restype = c_int 480 481PQsendDescribePortal = pq.PQsendDescribePortal 482PQsendDescribePortal.argtypes = [PGconn_ptr, c_char_p] 483PQsendDescribePortal.restype = c_int 484 485PQgetResult = pq.PQgetResult 486PQgetResult.argtypes = [PGconn_ptr] 487PQgetResult.restype = PGresult_ptr 488 489PQconsumeInput = pq.PQconsumeInput 490PQconsumeInput.argtypes = [PGconn_ptr] 491PQconsumeInput.restype = c_int 492 493PQisBusy = pq.PQisBusy 494PQisBusy.argtypes = [PGconn_ptr] 495PQisBusy.restype = c_int 496 497PQsetnonblocking = pq.PQsetnonblocking 498PQsetnonblocking.argtypes = [PGconn_ptr, c_int] 499PQsetnonblocking.restype = c_int 500 501PQisnonblocking = pq.PQisnonblocking 502PQisnonblocking.argtypes = [PGconn_ptr] 503PQisnonblocking.restype = c_int 504 505PQflush = pq.PQflush 506PQflush.argtypes = [PGconn_ptr] 507PQflush.restype = c_int 508 509 510# 33.5. Retrieving Query Results Row-by-Row 511PQsetSingleRowMode = pq.PQsetSingleRowMode 512PQsetSingleRowMode.argtypes = [PGconn_ptr] 513PQsetSingleRowMode.restype = c_int 514 515 516# 33.6. Canceling Queries in Progress 517 518PQgetCancel = pq.PQgetCancel 519PQgetCancel.argtypes = [PGconn_ptr] 520PQgetCancel.restype = PGcancel_ptr 521 522PQfreeCancel = pq.PQfreeCancel 523PQfreeCancel.argtypes = [PGcancel_ptr] 524PQfreeCancel.restype = None 525 526PQcancel = pq.PQcancel 527# TODO: raises "wrong type" error 528# PQcancel.argtypes = [PGcancel_ptr, POINTER(c_char), c_int] 529PQcancel.restype = c_int 530 531 532# 33.8. Asynchronous Notification 533 534PQnotifies = pq.PQnotifies 535PQnotifies.argtypes = [PGconn_ptr] 536PQnotifies.restype = PGnotify_ptr 537 538 539# 33.9. Functions Associated with the COPY Command 540 541PQputCopyData = pq.PQputCopyData 542PQputCopyData.argtypes = [PGconn_ptr, c_char_p, c_int] 543PQputCopyData.restype = c_int 544 545PQputCopyEnd = pq.PQputCopyEnd 546PQputCopyEnd.argtypes = [PGconn_ptr, c_char_p] 547PQputCopyEnd.restype = c_int 548 549PQgetCopyData = pq.PQgetCopyData 550PQgetCopyData.argtypes = [PGconn_ptr, POINTER(c_char_p), c_int] 551PQgetCopyData.restype = c_int 552 553 554# 33.11. Miscellaneous Functions 555 556PQfreemem = pq.PQfreemem 557PQfreemem.argtypes = [c_void_p] 558PQfreemem.restype = None 559 560if libpq_version >= 100000: 561 _PQencryptPasswordConn = pq.PQencryptPasswordConn 562 _PQencryptPasswordConn.argtypes = [ 563 PGconn_ptr, 564 c_char_p, 565 c_char_p, 566 c_char_p, 567 ] 568 _PQencryptPasswordConn.restype = POINTER(c_char) 569 570 571def PQencryptPasswordConn( 572 pgconn: PGconn_struct, passwd: bytes, user: bytes, algorithm: bytes 573) -> Optional[bytes]: 574 if not _PQencryptPasswordConn: 575 raise NotSupportedError( 576 f"PQencryptPasswordConn requires libpq from PostgreSQL 10," 577 f" {libpq_version} available instead" 578 ) 579 580 return _PQencryptPasswordConn(pgconn, passwd, user, algorithm) 581 582 583PQmakeEmptyPGresult = pq.PQmakeEmptyPGresult 584PQmakeEmptyPGresult.argtypes = [PGconn_ptr, c_int] 585PQmakeEmptyPGresult.restype = PGresult_ptr 586 587PQsetResultAttrs = pq.PQsetResultAttrs 588PQsetResultAttrs.argtypes = [PGresult_ptr, c_int, PGresAttDesc_ptr] 589PQsetResultAttrs.restype = c_int 590 591 592# 33.12. Notice Processing 593 594PQnoticeReceiver = CFUNCTYPE(None, c_void_p, PGresult_ptr) 595 596PQsetNoticeReceiver = pq.PQsetNoticeReceiver 597PQsetNoticeReceiver.argtypes = [PGconn_ptr, PQnoticeReceiver, c_void_p] 598PQsetNoticeReceiver.restype = PQnoticeReceiver 599 600# 34.5 Pipeline Mode 601 602_PQpipelineStatus = None 603_PQenterPipelineMode = None 604_PQexitPipelineMode = None 605_PQpipelineSync = None 606_PQsendFlushRequest = None 607 608if libpq_version >= 140000: 609 _PQpipelineStatus = pq.PQpipelineStatus 610 _PQpipelineStatus.argtypes = [PGconn_ptr] 611 _PQpipelineStatus.restype = c_int 612 613 _PQenterPipelineMode = pq.PQenterPipelineMode 614 _PQenterPipelineMode.argtypes = [PGconn_ptr] 615 _PQenterPipelineMode.restype = c_int 616 617 _PQexitPipelineMode = pq.PQexitPipelineMode 618 _PQexitPipelineMode.argtypes = [PGconn_ptr] 619 _PQexitPipelineMode.restype = c_int 620 621 _PQpipelineSync = pq.PQpipelineSync 622 _PQpipelineSync.argtypes = [PGconn_ptr] 623 _PQpipelineSync.restype = c_int 624 625 _PQsendFlushRequest = pq.PQsendFlushRequest 626 _PQsendFlushRequest.argtypes = [PGconn_ptr] 627 _PQsendFlushRequest.restype = c_int 628 629 630def PQpipelineStatus(pgconn: PGconn_struct) -> int: 631 if not _PQpipelineStatus: 632 raise NotSupportedError( 633 f"PQpipelineStatus requires libpq from PostgreSQL 14," 634 f" {libpq_version} available instead" 635 ) 636 return _PQpipelineStatus(pgconn) 637 638 639def PQenterPipelineMode(pgconn: PGconn_struct) -> int: 640 if not _PQenterPipelineMode: 641 raise NotSupportedError( 642 f"PQenterPipelineMode requires libpq from PostgreSQL 14," 643 f" {libpq_version} available instead" 644 ) 645 return _PQenterPipelineMode(pgconn) 646 647 648def PQexitPipelineMode(pgconn: PGconn_struct) -> int: 649 if not _PQexitPipelineMode: 650 raise NotSupportedError( 651 f"PQexitPipelineMode requires libpq from PostgreSQL 14," 652 f" {libpq_version} available instead" 653 ) 654 return _PQexitPipelineMode(pgconn) 655 656 657def PQpipelineSync(pgconn: PGconn_struct) -> int: 658 if not _PQpipelineSync: 659 raise NotSupportedError( 660 f"PQpipelineSync requires libpq from PostgreSQL 14," 661 f" {libpq_version} available instead" 662 ) 663 return _PQpipelineSync(pgconn) 664 665 666def PQsendFlushRequest(pgconn: PGconn_struct) -> int: 667 if not _PQsendFlushRequest: 668 raise NotSupportedError( 669 f"PQsendFlushRequest requires libpq from PostgreSQL 14," 670 f" {libpq_version} available instead" 671 ) 672 return _PQsendFlushRequest(pgconn) 673 674 675# 33.18. SSL Support 676 677PQinitOpenSSL = pq.PQinitOpenSSL 678PQinitOpenSSL.argtypes = [c_int, c_int] 679PQinitOpenSSL.restype = None 680 681 682def generate_stub() -> None: 683 import re 684 from ctypes import _CFuncPtr # type: ignore 685 686 def type2str(fname, narg, t): 687 if t is None: 688 return "None" 689 elif t is c_void_p: 690 return "Any" 691 elif t is c_int or t is c_uint or t is c_size_t: 692 return "int" 693 elif t is c_char_p or t.__name__ == "LP_c_char": 694 if narg is not None: 695 return "bytes" 696 else: 697 return "Optional[bytes]" 698 699 elif t.__name__ in ( 700 "LP_PGconn_struct", 701 "LP_PGresult_struct", 702 "LP_PGcancel_struct", 703 ): 704 if narg is not None: 705 return f"Optional[{t.__name__[3:]}]" 706 else: 707 return t.__name__[3:] 708 709 elif t.__name__ in ("LP_PQconninfoOption_struct",): 710 return f"Sequence[{t.__name__[3:]}]" 711 712 elif t.__name__ in ( 713 "LP_c_ubyte", 714 "LP_c_char_p", 715 "LP_c_int", 716 "LP_c_uint", 717 "LP_c_ulong", 718 ): 719 return f"pointer[{t.__name__[3:]}]" 720 721 else: 722 assert False, f"can't deal with {t} in {fname}" 723 724 fn = __file__ + "i" 725 with open(fn) as f: 726 lines = f.read().splitlines() 727 728 istart, iend = ( 729 i 730 for i, line in enumerate(lines) 731 if re.match(r"\s*#\s*autogenerated:\s+(start|end)", line) 732 ) 733 734 known = { 735 line[4:].split("(", 1)[0] 736 for line in lines[:istart] 737 if line.startswith("def ") 738 } 739 740 signatures = [] 741 742 for name, obj in globals().items(): 743 if name in known: 744 continue 745 if not isinstance(obj, _CFuncPtr): 746 continue 747 748 params = [] 749 for i, t in enumerate(obj.argtypes): 750 params.append(f"arg{i + 1}: {type2str(name, i, t)}") 751 752 resname = type2str(name, None, obj.restype) 753 754 signatures.append(f"def {name}({', '.join(params)}) -> {resname}: ...") 755 756 lines[istart + 1 : iend] = signatures 757 758 with open(fn, "w") as f: 759 f.write("\n".join(lines)) 760 f.write("\n") 761 762 763if __name__ == "__main__": 764 generate_stub() 765