4646if  TYPE_CHECKING :
4747    from  rich .style  import  StyleType 
4848
49-     YAMLStyle  =  Literal ["" , "'" , '"' , "|" , ">" ]
50- 
5149
5250class  CLIDefaults :
5351    INDENT  =  None 
5452    SORT_KEYS  =  False 
5553    STRINGIFY  =  False 
56-     WIDTH  =  80 
5754
5855
5956class  Defaults :
6057    JSON_INDENT  =  4 
6158    MAX_VALUES  =  1000000 
6259    YAML_INDENT  =  2 
6360    YAML_STYLE  =  "" 
61+     WIDTH  =  80 
62+ 
63+     PYTHON_WIDTH  =  WIDTH 
64+     YAML_WIDTH  =  WIDTH 
65+ 
66+ 
67+ YAMLStyle  =  Literal ["" , "'" , '"' , "|" , ">" ]
6468
6569
6670@dataclass (frozen = True ) 
6771class  FormatOptions :
6872    pass 
6973
7074
75+ @dataclass (frozen = True ) 
76+ class  CBOROptions (FormatOptions ):
77+     pass 
78+ 
79+ 
80+ @dataclass (frozen = True ) 
81+ class  JSONOptions (FormatOptions ):
82+     indent : int  |  None  =  Defaults .JSON_INDENT 
83+     sort_keys : bool  =  True 
84+     stringify : bool  =  False 
85+ 
86+ 
87+ @dataclass (frozen = True ) 
88+ class  MsgPackOptions (FormatOptions ):
89+     pass 
90+ 
91+ 
92+ @dataclass (frozen = True ) 
93+ class  PythonOptions (FormatOptions ):
94+     indent : int  |  None  =  None 
95+     sort_keys : bool  =  True 
96+     width : int  =  Defaults .PYTHON_WIDTH 
97+ 
98+ 
99+ @dataclass (frozen = True ) 
100+ class  TOMLOptions (FormatOptions ):
101+     indent : int  |  None  =  None 
102+     sort_keys : bool  =  True 
103+     stringify : bool  =  False 
104+ 
105+ 
71106@dataclass (frozen = True ) 
72107class  YAMLOptions (FormatOptions ):
108+     indent : int  =  Defaults .YAML_INDENT 
73109    style : YAMLStyle  =  Defaults .YAML_STYLE 
110+     width : int  =  Defaults .YAML_WIDTH 
74111
75112
76113__all__  =  [
77114    "INPUT_FORMATS" ,
115+     "OPTIONS_CLASSES" ,
78116    "OUTPUT_FORMATS" ,
79117    "RICH_ARGPARSE_STYLES" ,
80118    "CLIDefaults" ,
@@ -85,6 +123,7 @@ class YAMLOptions(FormatOptions):
85123    "YAMLOptions" ,
86124    "decode" ,
87125    "encode" ,
126+     "format_options" ,
88127    "identity" ,
89128    "main" ,
90129    "remarshal" ,
@@ -94,6 +133,14 @@ class YAMLOptions(FormatOptions):
94133
95134INPUT_FORMATS  =  ["cbor" , "json" , "msgpack" , "toml" , "yaml" ]
96135OUTPUT_FORMATS  =  ["cbor" , "json" , "msgpack" , "python" , "toml" , "yaml" ]
136+ OPTIONS_CLASSES  =  {
137+     "cbor" : CBOROptions ,
138+     "json" : JSONOptions ,
139+     "msgpack" : MsgPackOptions ,
140+     "python" : PythonOptions ,
141+     "toml" : TOMLOptions ,
142+     "yaml" : YAMLOptions ,
143+ }
97144UTF_8  =  "utf-8" 
98145
99146RICH_ARGPARSE_STYLES : dict [str , StyleType ] =  {
@@ -289,7 +336,7 @@ def output_width(value: str) -> int:
289336
290337    parser .add_argument (
291338        "--width" ,
292-         default = CLIDefaults .WIDTH ,
339+         default = Defaults .WIDTH ,
293340        metavar = "<n>" ,
294341        type = output_width ,  # Allow "inf". 
295342        help = (
@@ -351,13 +398,6 @@ def output_width(value: str) -> int:
351398            if  args .output_format  ==  "" :
352399                parser .error ("Need an explicit output format" )
353400
354-     # Replace `yaml_*` options with `YAMLOptions`. 
355-     vars (args )["yaml_options" ] =  YAMLOptions (
356-         style = args .yaml_style ,
357-     )
358- 
359-     del  vars (args )["yaml_style" ]
360- 
361401    return  args 
362402
363403
@@ -622,22 +662,21 @@ def _encode_python(
622662    indent : int  |  None ,
623663    sort_keys : bool ,
624664    width : int ,
625- ) ->  bytes :
665+ ) ->  str :
626666    compact  =  False 
627667    if  indent  is  None :
628668        compact  =  True 
629669        indent  =  0 
630670
631-     return  bytes (
671+     return  (
632672        pprint .pformat (
633673            data ,
634674            compact = compact ,
635675            indent = indent ,
636676            sort_dicts = sort_keys ,
637677            width = width ,
638678        )
639-         +  "\n " ,
640-         UTF_8 ,
679+         +  "\n " 
641680    )
642681
643682
@@ -695,20 +734,13 @@ def _encode_yaml(
695734    data : Document ,
696735    * ,
697736    indent : int  |  None ,
698-     options :  FormatOptions   |   None ,
737+     style :  YAMLStyle ,
699738    width : int ,
700739) ->  str :
701-     if  options  is  None :
702-         options  =  YAMLOptions ()
703- 
704-     if  not  isinstance (options , YAMLOptions ):
705-         msg  =  "'options' not of type 'YAMLOptions'" 
706-         raise  TypeError (msg )
707- 
708740    yaml  =  ruamel .yaml .YAML ()
709741    yaml .default_flow_style  =  False 
710742
711-     yaml .default_style  =  options . style   # type: ignore 
743+     yaml .default_style  =  style   # type: ignore 
712744    yaml .indent  =  indent 
713745    yaml .width  =  width 
714746
@@ -728,53 +760,123 @@ def _encode_yaml(
728760        raise  ValueError (msg )
729761
730762
763+ def  format_options (
764+     output_format : str ,
765+     * ,
766+     indent : int  |  None  =  None ,
767+     sort_keys : bool  =  False ,
768+     stringify : bool  =  False ,
769+     width : int  =  Defaults .WIDTH ,
770+     yaml_style : YAMLStyle  =  Defaults .YAML_STYLE ,
771+ ) ->  FormatOptions :
772+     if  output_format  ==  "cbor" :
773+         return  CBOROptions ()
774+ 
775+     if  output_format  ==  "json" :
776+         return  JSONOptions (
777+             indent = indent ,
778+             sort_keys = sort_keys ,
779+             stringify = stringify ,
780+         )
781+ 
782+     if  output_format  ==  "msgpack" :
783+         return  MsgPackOptions ()
784+ 
785+     if  output_format  ==  "python" :
786+         return  PythonOptions (
787+             indent = indent ,
788+             sort_keys = sort_keys ,
789+             width = width ,
790+         )
791+ 
792+     if  output_format  ==  "toml" :
793+         return  TOMLOptions (
794+             sort_keys = sort_keys ,
795+             stringify = stringify ,
796+         )
797+ 
798+     if  output_format  ==  "yaml" :
799+         return  YAMLOptions (
800+             indent = Defaults .YAML_INDENT  if  indent  is  None  else  indent ,
801+             style = yaml_style ,
802+             width = width ,
803+         )
804+ 
805+     msg  =  f"Unknown output format: { output_format }  
806+     raise  ValueError (msg )
807+ 
808+ 
731809def  encode (
732810    output_format : str ,
733811    data : Document ,
734812    * ,
735-     indent : int  |  None ,
736813    options : FormatOptions  |  None ,
737-     sort_keys : bool ,
738-     stringify : bool ,
739-     width : int ,
740814) ->  bytes :
741-     if  output_format  ==  "json" :
815+     if  output_format  ==  "cbor" :
816+         if  not  isinstance (options , CBOROptions ):
817+             msg  =  "expected 'options' argument to have class 'CBOROptions'" 
818+             raise  TypeError (msg )
819+ 
820+         encoded  =  _encode_cbor (data )
821+ 
822+     elif  output_format  ==  "json" :
823+         if  not  isinstance (options , JSONOptions ):
824+             msg  =  "expected 'options' argument to have class 'JSONOptions'" 
825+             raise  TypeError (msg )
826+ 
742827        encoded  =  _encode_json (
743828            data ,
744-             indent = indent ,
745-             sort_keys = sort_keys ,
746-             stringify = stringify ,
829+             indent = options . indent ,
830+             sort_keys = options . sort_keys ,
831+             stringify = options . stringify ,
747832        ).encode (UTF_8 )
833+ 
748834    elif  output_format  ==  "msgpack" :
835+         if  not  isinstance (options , MsgPackOptions ):
836+             msg  =  "expected 'options' argument to have class 'MsgPackOptions'" 
837+             raise  TypeError (msg )
749838        encoded  =  _encode_msgpack (data )
839+ 
750840    elif  output_format  ==  "python" :
841+         if  not  isinstance (options , PythonOptions ):
842+             msg  =  "expected 'options' argument to have class 'PythonOptions'" 
843+             raise  TypeError (msg )
751844        encoded  =  _encode_python (
752845            data ,
753-             indent = indent ,
754-             sort_keys = sort_keys ,
755-             width = width ,
756-         )
846+             indent = options .indent ,
847+             sort_keys = options .sort_keys ,
848+             width = options .width ,
849+         ).encode (UTF_8 )
850+ 
757851    elif  output_format  ==  "toml" :
852+         if  not  isinstance (options , TOMLOptions ):
853+             msg  =  "expected 'options' argument to have class 'TOMLOptions'" 
854+             raise  TypeError (msg )
855+ 
758856        if  not  isinstance (data , Mapping ):
759857            msg  =  (
760858                f"Top-level value of type '{ type (data ).__name__ }  
761859                "be encoded as TOML" 
762860            )
763861            raise  TypeError (msg )
764-         encoded  =  _encode_toml (data , sort_keys = sort_keys , stringify = stringify ).encode (
765-             UTF_8 
766-         )
862+         encoded  =  _encode_toml (
863+             data ,
864+             sort_keys = options .sort_keys ,
865+             stringify = options .stringify ,
866+         ).encode (UTF_8 )
867+ 
767868    elif  output_format  ==  "yaml" :
869+         if  not  isinstance (options , YAMLOptions ):
870+             msg  =  "expected 'options' argument to have class 'YAMLOptions'" 
871+             raise  TypeError (msg )
872+ 
768873        encoded  =  _encode_yaml (
769874            data ,
770-             indent = indent ,
771-             options = options ,
772-             width = width ,
875+             indent = options . indent ,
876+             style = options . style ,
877+             width = options . width ,
773878        ).encode (UTF_8 )
774-     elif  output_format  ==  "msgpack" :
775-         encoded  =  _encode_msgpack (data )
776-     elif  output_format  ==  "cbor" :
777-         encoded  =  _encode_cbor (data )
879+ 
778880    else :
779881        msg  =  f"Unknown output format: { output_format }  
780882        raise  ValueError (msg )
@@ -785,32 +887,31 @@ def encode(
785887# === Main === 
786888
787889
788- def  remarshal (   # noqa: PLR0913 
890+ def  remarshal (
789891    input_format : str ,
790892    output_format : str ,
791893    input : Path  |  str ,
792894    output : Path  |  str ,
793895    * ,
794-     indent : int  |  None  =  None ,
795896    max_values : int  =  Defaults .MAX_VALUES ,
796897    options : FormatOptions  |  None  =  None ,
797-     sort_keys : bool  =  True ,
798-     stringify : bool  =  False ,
799898    transform : Callable [[Document ], Document ] |  None  =  None ,
800899    unwrap : str  |  None  =  None ,
801-     width : int  =  CLIDefaults .WIDTH ,
802900    wrap : str  |  None  =  None ,
803901) ->  None :
804902    input_file  =  None 
805903    output_file  =  None 
806904
905+     if  options  is  None :
906+         options  =  format_options (output_format )
907+ 
807908    try :
808909        input_file  =  sys .stdin .buffer  if  input  ==  "-"  else  Path (input ).open ("rb" )
809910        output_file  =  sys .stdout .buffer  if  output  ==  "-"  else  Path (output ).open ("wb" )
810911
811912        input_data  =  input_file .read ()
812913        if  not  isinstance (input_data , bytes ):
813-             msg  =  "input_data must be bytes" 
914+             msg  =  "' input_data'  must be ' bytes' " 
814915            raise  TypeError (msg )
815916
816917        parsed  =  decode (input_format , input_data )
@@ -836,11 +937,7 @@ def remarshal(  # noqa: PLR0913
836937        encoded  =  encode (
837938            output_format ,
838939            parsed ,
839-             indent = indent ,
840940            options = options ,
841-             sort_keys = sort_keys ,
842-             stringify = stringify ,
843-             width = width ,
844941        )
845942
846943        output_file .write (encoded )
@@ -855,16 +952,22 @@ def main() -> None:
855952    args  =  _parse_command_line (sys .argv )
856953
857954    try :
955+         options  =  format_options (
956+             args .output_format ,
957+             indent = args .indent ,
958+             sort_keys = args .sort_keys ,
959+             stringify = args .stringify ,
960+             width = args .width ,
961+             yaml_style = args .yaml_style ,
962+         )
963+ 
858964        remarshal (
859965            args .input_format ,
860966            args .output_format ,
861967            args .input ,
862968            args .output ,
863-             indent = args .indent ,
864969            max_values = args .max_values ,
865-             options = args .yaml_options ,
866-             sort_keys = args .sort_keys ,
867-             stringify = args .stringify ,
970+             options = options ,
868971            unwrap = args .unwrap ,
869972            wrap = args .wrap ,
870973        )
0 commit comments