1414
1515from __future__ import annotations
1616
17+ import enum
1718import typing
18- from typing import Any , List , Sequence , Union
19+ from typing import List , Sequence , Union
1920
2021import bigframes_vendored .constants as constants
2122import bigframes_vendored .pandas .pandas ._typing as vendored_pandas_typing
3435import bigframes .series as series
3536import bigframes .session
3637
37- _NO_NAME_SENTINEL = object ()
38+
39+ class Default (enum .Enum ):
40+ """Sentinel that can disambiguate explicit None from missing.
41+
42+ See https://stackoverflow.com/a/76606310/101923
43+ """
44+
45+ token = 0
46+
47+
48+ DEFAULT = Default .token
3849
3950
4051class SeriesMethods :
@@ -45,7 +56,7 @@ def __init__(
4556 dtype : typing .Optional [
4657 bigframes .dtypes .DtypeString | bigframes .dtypes .Dtype
4758 ] = None ,
48- name : str | None = None ,
59+ name : str | None | Default = DEFAULT ,
4960 copy : typing .Optional [bool ] = None ,
5061 * ,
5162 session : typing .Optional [bigframes .session .Session ] = None ,
@@ -73,6 +84,16 @@ def __init__(
7384 f"Series constructor only supports copy=True. { constants .FEEDBACK_LINK } "
7485 )
7586
87+ if name is DEFAULT :
88+ if isinstance (data , blocks .Block ):
89+ name = data .column_labels [0 ]
90+ elif hasattr (data , "name" ):
91+ name = getattr (data , "name" )
92+ elif hasattr (data , "_name" ):
93+ name = getattr (data , "_name" )
94+ else :
95+ name = None
96+
7697 if isinstance (data , blocks .Block ):
7798 block = data
7899 elif isinstance (data , SeriesMethods ):
@@ -109,6 +130,7 @@ def __init__(
109130 block = data_block
110131
111132 if block :
133+ # Data was a bigframes object.
112134 assert len (block .value_columns ) == 1
113135 assert len (block .column_labels ) == 1
114136 if index is not None : # reindexing operation
@@ -121,6 +143,7 @@ def __init__(
121143 bf_dtype = bigframes .dtypes .bigframes_type (dtype )
122144 block = block .multi_apply_unary_op (ops .AsTypeOp (to_type = bf_dtype ))
123145 else :
146+ # Data was local.
124147 if isinstance (dtype , str ) and dtype .lower () == "json" :
125148 dtype = bigframes .dtypes .JSON_DTYPE
126149 pd_series = pd .Series (
@@ -129,25 +152,12 @@ def __init__(
129152 dtype = dtype , # type:ignore
130153 name = name ,
131154 )
155+ name = pd_series .name # type: ignore
132156 block = read_pandas_func (pd_series )._get_block () # type:ignore
133157
134158 assert block is not None
135159
136- # If we didn't get a block make sure the name is what the user
137- # explicitly chose even if it is None. This is important for the
138- # polars backend where the implicit column labels are integers.
139- if name :
140- default_name : Any = name
141- elif hasattr (data , "name" ):
142- default_name = getattr (data , "name" , None )
143- elif hasattr (data , "_name" ):
144- default_name = getattr (data , "_name" , None )
145- else :
146- default_name = _NO_NAME_SENTINEL
147-
148- if default_name is not _NO_NAME_SENTINEL :
149- block = block .with_column_labels ([default_name ])
150-
160+ block = block .with_column_labels ([name ])
151161 self ._block : blocks .Block = block
152162
153163 @property
0 commit comments