@@ -156,7 +156,33 @@ ${headerString}${bodyString}`;
156156 syntax : 'python' ,
157157 generate : ( { method, url : { origin, path } , headers, body } ) => {
158158 const contentType = headers ?. [ 'Content-Type' ] ;
159- let code = `${ isJSON ( contentType ) ? 'import json\n' : '' } import requests\n\n` ;
159+ const needsJsonImport = body && isJSON ( contentType ) && typeof body === 'string' ;
160+
161+ let code = '' ;
162+
163+ // Import statements
164+ if ( needsJsonImport ) {
165+ code += 'import json\n' ;
166+ }
167+ code += 'import requests\n\n' ;
168+
169+ // Extract path parameters and create constants
170+ const { extractedParams, processedPath } = extractPathParameters ( path ) ;
171+ if ( extractedParams . length > 0 ) {
172+ extractedParams . forEach ( param => {
173+ code += `${ param . constant } = "${ param . placeholder } "\n` ;
174+ } ) ;
175+ code += '\n' ;
176+ }
177+
178+ // Process headers to create better placeholders
179+ const processedHeaders = processPythonHeaders ( headers ) ;
180+ if ( processedHeaders . constants . length > 0 ) {
181+ processedHeaders . constants . forEach ( constant => {
182+ code += `${ constant . name } = "${ constant . placeholder } "\n` ;
183+ } ) ;
184+ code += '\n' ;
185+ }
160186
161187 if ( body ) {
162188 const lines = BodyGenerators . getPythonBody ( body , headers ) ;
@@ -169,17 +195,31 @@ ${headerString}${bodyString}`;
169195 }
170196 }
171197
198+ // Build the request
199+ const urlStr = extractedParams . length > 0
200+ ? `f"${ origin } ${ processedPath } "`
201+ : `"${ origin } ${ path } "` ;
202+
172203 code += `response = requests.${ method . toLowerCase ( ) } (\n` ;
173- code += indent ( `" ${ origin } ${ path } " ,\n` , 4 ) ;
204+ code += indent ( `${ urlStr } ,\n` , 4 ) ;
174205
175- if ( headers && Object . keys ( headers ) . length > 0 ) {
176- code += indent ( `headers=${ stringifyOpenAPI ( headers ) } ,\n` , 4 ) ;
206+ if ( processedHeaders . headers && Object . keys ( processedHeaders . headers ) . length > 0 ) {
207+ code += indent ( `headers={\n` , 4 ) ;
208+ Object . entries ( processedHeaders . headers ) . forEach ( ( [ key , value ] , index , array ) => {
209+ const isLast = index === array . length - 1 ;
210+ code += indent ( `"${ key } ": ${ value } ${ isLast ? '' : ',' } \n` , 8 ) ;
211+ } ) ;
212+ code += indent ( `},\n` , 4 ) ;
177213 }
178214
179215 if ( body ) {
180216 if ( body === 'files' ) {
181217 code += indent ( `files=${ body } \n` , 4 ) ;
182- } else if ( isJSON ( contentType ) ) {
218+ } else if ( isJSON ( contentType ) && isPlainObject ( body ) ) {
219+ // Use json parameter for dict objects
220+ code += indent ( `json=${ body } \n` , 4 ) ;
221+ } else if ( isJSON ( contentType ) && needsJsonImport ) {
222+ // Use data=json.dumps() for JSON strings
183223 code += indent ( `data=json.dumps(${ body } )\n` , 4 ) ;
184224 } else {
185225 code += indent ( `data=${ body } \n` , 4 ) ;
@@ -372,7 +412,29 @@ const BodyGenerators = {
372412 } else if ( isYAML ( contentType ) ) {
373413 code += `yamlBody = \"\"\"\n${ indent ( yaml . dump ( body ) , 4 ) } \"\"\"\n\n` ;
374414 body = 'yamlBody' ;
415+ } else if ( isJSON ( contentType ) && isPlainObject ( body ) ) {
416+ // For dict objects, return as-is to use with json= parameter
417+ body = stringifyOpenAPI (
418+ body ,
419+ ( _key , value ) => {
420+ switch ( value ) {
421+ case true :
422+ return '$$__TRUE__$$' ;
423+ case false :
424+ return '$$__FALSE__$$' ;
425+ case null :
426+ return '$$__NULL__$$' ;
427+ default :
428+ return value ;
429+ }
430+ } ,
431+ 2
432+ )
433+ . replaceAll ( '"$$__TRUE__$$"' , 'True' )
434+ . replaceAll ( '"$$__FALSE__$$"' , 'False' )
435+ . replaceAll ( '"$$__NULL__$$"' , 'None' ) ;
375436 } else {
437+ // For everything else (including JSON strings)
376438 body = stringifyOpenAPI (
377439 body ,
378440 ( _key , value ) => {
@@ -487,3 +549,80 @@ function buildHeredoc(lines: string[]): string {
487549 }
488550 return result ;
489551}
552+
553+ /**
554+ * Extracts path parameters and converts them to Python constants
555+ */
556+ function extractPathParameters ( path : string ) : {
557+ extractedParams : Array < { constant : string ; placeholder : string ; param : string } > ;
558+ processedPath : string ;
559+ } {
560+ const extractedParams : Array < { constant : string ; placeholder : string ; param : string } > = [ ] ;
561+ let processedPath = path ;
562+
563+ // Find all path parameters in the format {paramName}
564+ const paramMatches = path . match ( / \{ ( [ ^ } ] + ) \} / g) ;
565+
566+ if ( paramMatches ) {
567+ paramMatches . forEach ( match => {
568+ const paramName = match . slice ( 1 , - 1 ) ; // Remove { and }
569+ // Convert camelCase to SNAKE_CASE
570+ const constantName = paramName
571+ . replace ( / ( [ a - z ] ) ( [ A - Z ] ) / g, '$1_$2' )
572+ . toUpperCase ( ) ;
573+ const placeholder = `<your ${ paramName . replace ( / ( [ a - z ] ) ( [ A - Z ] ) / g, '$1 $2' ) . toLowerCase ( ) } >` ;
574+
575+ extractedParams . push ( {
576+ constant : constantName ,
577+ placeholder : placeholder ,
578+ param : paramName
579+ } ) ;
580+
581+ // Replace {paramName} with {CONSTANT_NAME} for f-string
582+ processedPath = processedPath . replace ( match , `{${ constantName } }` ) ;
583+ } ) ;
584+ }
585+
586+ return { extractedParams, processedPath } ;
587+ }
588+
589+ /**
590+ * Processes headers to create Python constants and clean formatting
591+ */
592+ function processPythonHeaders ( headers ?: Record < string , string > ) : {
593+ constants : Array < { name : string ; placeholder : string } > ;
594+ headers : Record < string , string > ;
595+ } {
596+ if ( ! headers ) {
597+ return { constants : [ ] , headers : { } } ;
598+ }
599+
600+ const constants : Array < { name : string ; placeholder : string } > = [ ] ;
601+ const processedHeaders : Record < string , string > = { } ;
602+
603+ Object . entries ( headers ) . forEach ( ( [ key , value ] ) => {
604+ if ( key === 'Authorization' && value . includes ( 'Bearer' ) ) {
605+ // Extract token constants
606+ const constantName = 'API_TOKEN' ;
607+ const placeholder = '<your gitbook api token>' ;
608+ constants . push ( { name : constantName , placeholder } ) ;
609+ processedHeaders [ key ] = `f"Bearer {${ constantName } }"` ;
610+ } else if ( key === 'Authorization' && value . includes ( 'Basic' ) ) {
611+ const constantName = 'API_TOKEN' ;
612+ const placeholder = '<your basic auth token>' ;
613+ constants . push ( { name : constantName , placeholder } ) ;
614+ processedHeaders [ key ] = `f"Basic {${ constantName } }"` ;
615+ } else if ( value . includes ( 'YOUR_' ) || value . includes ( 'TOKEN' ) ) {
616+ // Generic token handling
617+ const constantName = 'API_TOKEN' ;
618+ const placeholder = '<your api token>' ;
619+ constants . push ( { name : constantName , placeholder } ) ;
620+ processedHeaders [ key ] = `f"Bearer {${ constantName } }"` ;
621+ } else {
622+ // Regular headers
623+ processedHeaders [ key ] = `"${ value } "` ;
624+ }
625+ } ) ;
626+
627+ return { constants, headers : processedHeaders } ;
628+ }
0 commit comments