Drop CamelCase in favor of snake_case in future versions of Panda3D

There has been discussion from time to time about dropping CamelCase in Panda3D in favor of snake_case which also would be more “Pythonic” to begin with. For anyone who doesn’t know in what way it would be more Pythonic to use snake_case, please have a look at PEP 8. (admittedly, one can argue that PEP 8 was devised to keep the Python Standard Library on a single coding style, though IMHO it is a proven and good style convention to follow also outside of the standard library)

This would entail the following:

  • All methods in Panda3D would be by default in snake_case (e.g. loader.load_model instead of loader.loadModel) => There are a limited amount of cases left, where only CamelCase is available currently, otherwise snake_case is almost entirely available in Panda3D as of the time of this writing
  • All member variables => This would be a potentially/likely breaking change and ample advanced notice should be given. My suggestion would be to announce it 2 releases prior to it taking effect
  • All function arguments => same as member variables

There are ideas floating around to keep both notations around for legacy purposes and since I’m neither a core dev of Panda3D nor do I have any big project that couldn’t be updated with some easy refactoring if CamelCase were to be dropped, I started this discussion to hear from other Panda3D users to collect suggestions, objections, critique and so forth.

Would this just be fore Python code or for the C++ code base as well?

The C++ code is actually already snake_case afaik. Interrogate just wraps around CamelCase at the moment, but that can be changed w/o problems in future versions.

That… would be a pain for me. I use camel-case, not snake-case, and I don’t want to either refactor my projects nor change my coding style–especially as the reasons given for the change aren’t particularly compelling to me. And especially so if the renaming might break things!

(PEP’s recommendations are suggestions, as far as I’m concerned; I see little particularly good reason to hew to them or eschew them, beyond personal preference.)

Unless this would cause additional trouble for the developers of the engine, I would ask that camel-case be preserved for those who prefer it.

(One of my projects is quite large, but since you mention this taking place in at most two releases, I don’t know whether it would be affected.)

I was in the camelCase camp for a long time but not anymore, but
I’m not sure that breaking a lot of existing code for pep8-sake is worth it.
Would converting to snake_case and having camelCase aliases (opposite of what we have now) be a problem?

As a side note - I think the snake case constants used in p3d are not formatted according to PEP8 - eg FTLinearMipmapLinear became FT_linear_mipmap_linear but it should be FT_LINEAR_MIPMAP_LINEAR

I don’t want to create any confusion and following PEP8 in the API wasn’t meant to enforce its use in Panda3D, but it was probably poorly worded by me in the first post. Rather how the API at this point would be getting inconsistent in naming, with the efforts of adding snake_case. So to clarify:

  • Method names in camelCase would still be available and there isn’t any need to drop those as far as I can tell, but the API reference would/could be switched to snake_case as default w/o much effort. (being the part I wanted to point out as following PEP8)


  • Members (e.g. base.camNode) and function arguments (base.make_camera(aspectRatio=12.0)) are the main issue here. Those would need renaming to stay consistent in the API, if the switch of snake_case were to be flipped. (There are ideas floating around to enable both, but all would come at the price of additional overhead for existing Panda apps and/or increased code complexity of the Panda3D source code)

It might not be worth going further than just adding all currently missing aliases in snake_case for methods and leave the Python API unchanged (as in default being camelCase), if the feedback were to be generally a big NO.

@Thaumaturge: given my clarification above, am I right to assume, that this still would be a pain for you?
@wezu: Given that camelCase methods would remain, do you think it unreasonable to make such a change over the period of 2 release cycles? There might even be a direct replacement by then, so I figure that most legacy code would either be run in older P3D versions or ported to use new capabilities, but again, I could be dead wrong…

In fact, it is necessary to follow the PEP8 in the collective development of the project.
It would be better to change the names of the classes and methods to more logical ones. For example, everyone is familiar with pain?

If I understand correctly, it would be less of a pain (as my method-names would at least be fine), but still a pain (as quite a few variable-uses and keyword-parameters would be broken), indeed.

This, on the other hand, I’m in favour of. (And indeed, I’m a little surprised that there are un-aliased methods!) That would at least allow those who prefer snake-case to use it consistently, without being required to occasionally use camel-case.

(Except, alas, for the aforementioned parameters and variables, which is a pity, I will admit. :/)

I would argue that, when developing collaboratively, it’s a good idea to stick to a common style. However, that needn’t be PEP8’s style–just whatever the development group decides on. (And it doesn’t rule out aliases, such as we already use.)

There is an argument for the problem of integrating third-party snake-case code into a camel-case project.

I suppose that, in part, I just don’t like arbitrary decisions being determined by external bodies.

Hah, I wouldn’t mind seeing those things changed, I think! (Well, if it weren’t for all of the stuff that it might break!)

I think we should eventually settle on snake_case, not only because it’s what Panda’s C++ codebase uses (and has always used), but also because it’s what the vast majority of other Python projects uses (PEP8 or not).

Having to maintain aliases is awkward, and adds bloat for no good technical reason.

As developers, do as you need. I do not think it will be worse.

Hmm… That, I will confess, is a compelling set of arguments. Fair enough then, if the developers decide to switch over to snake-case, I won’t gainsay it.

I imagine it would not be hard to write a script like 2to3 to change any source code from camel to snake case.

I’m not against the change and I try tu use snake case all the time anyway.

1 Like

Perhaps you can find what you need here:

@Thaumaturge: I understand your reluctance to adopt snake_case very well; I’ve been there too :wink: . But a certain rdb nevertheless managed to convert me to a snake_case proponent - and in turn I converted my code :slight_smile: .

1 Like

The funny thing is, I think that I used to use snake-case, but switched to camel-case at some point (possibly because of Panda3D)! :stuck_out_tongue:

[edit] “Some point” being ages ago, now–well before my current projects, I think.

Like @wezu pointed out, having a script to do the refactoring for Panda3D specific method, argument and member names could be part of that goal to move to a snake_case only API.
I have worked with regex in the past in Python and feel comfortable to look for patterns and replace suitably, but I suspect some potential pitfalls in the task. It isn’t just a matter of doing a simple find and replace, instead I believe it would need at least rudimentary parsing capabilities to follow the code and not to mangle parts that should be left alone (e.g. comments, strings, etc.).

To encounter and prevent those pitfalls, next to working out a suitable design, much testing on as many different code bases as possible would have to be done, once we have an initial version of the script, to guarantee a sufficiently high level of certainty, that using the script won’t just create more work to the user than manually refactoring of a code base.

Anyone with a lot of experience in scripted refactoring of large code bases here maybe to chime in?

I really did not do this, but at the time when I was programming the site of the engine. I used PHP, it will cope. This is actually a regular replacement for text pattern. The main thing is to make a list of names for replacement.

I typed an example in PHP and, of course, it works, I think that Python will cope too. The problem with comments and lines is contrived, you can form a report about where and what was replaced, check manually and correct if necessary. This is much less than replacing the code for all the lines manually.

Demo: http://codigr.ru/ (web convertet)

This is certainly not serious. Changes only 4 names for the code. https://www.panda3d.org/manual/?title=Loading_the_Grassy_Scenery


Does anyone know how to get all the names that need to be replaced in the style of a camel? Of course, it would be better and option in the style of a snake.

What comes to mind is the parsing API for example a class https://www.panda3d.org/reference/python/classdirect_1_1actor_1_1Actor_1_1Actor.html
and convert names with this function to create a replacement base.

Although I see the work already done the names are mapped in the column “Static Public Attributes”

Heh, it will be easy. Although due to the lack of such a column, for some it will not be so easy.

Oops, it seems need a python with the dir () function

The first results of the parser for matching pairs Camel and Snake, on class Actor.

--- No snake style ---
--- There is a snake style ---
ETFail -> ET_fail
ETNotFound -> ET_not_found
ETOk -> ET_ok
ETRemoved -> ET_removed
acceptOnce -> accept_once
actorInterval -> actor_interval
addHash -> add_hash
addLOD -> add_lod
addTask -> add_task
adjustAllPriorities -> adjust_all_priorities
animPanel -> anim_panel
anyPath -> any_path
applyTextureColors -> apply_texture_colors
attachNewNode -> attach_new_node
bindAllAnims -> bind_all_anims
bindAnim -> bind_anim
calcTightBounds -> calc_tight_bounds
clearAntialias -> clear_antialias
clearAttrib -> clear_attrib
clearAudioVolume -> clear_audio_volume
clearBillboard -> clear_billboard
clearBin -> clear_bin
clearClipPlane -> clear_clip_plane
clearColor -> clear_color
clearColorScale -> clear_color_scale
clearCompass -> clear_compass
clearDepthOffset -> clear_depth_offset
clearDepthTest -> clear_depth_test
clearDepthWrite -> clear_depth_write
clearEffect -> clear_effect
clearEffects -> clear_effects
clearFog -> clear_fog
clearLODAnimation -> clear_lod_animation
clearLight -> clear_light
clearLogicOp -> clear_logic_op
clearMat -> clear_mat
clearMaterial -> clear_material
clearModelNodes -> clear_model_nodes
clearOccluder -> clear_occluder
clearProjectTexture -> clear_project_texture
clearPythonData -> clear_python_data
clearPythonTag -> clear_python_tag
clearRenderMode -> clear_render_mode
clearScissor -> clear_scissor
clearShader -> clear_shader
clearShaderInput -> clear_shader_input
clearTag -> clear_tag
clearTexGen -> clear_tex_gen
clearTexProjector -> clear_tex_projector
clearTexTransform -> clear_tex_transform
clearTexture -> clear_texture
clearTransform -> clear_transform
clearTransparency -> clear_transparency
clearTwoSided -> clear_two_sided
compareTo -> compare_to
composeColorScale -> compose_color_scale
controlJoint -> control_joint
copyActor -> copy_actor
copyTo -> copy_to
countNumDescendants -> count_num_descendants
decodeFromBamStream -> decode_from_bam_stream
detachNode -> detach_node
detectLeaks -> detect_leaks
disableBlend -> disable_blend
doBillboardAxis -> do_billboard_axis
doBillboardPointEye -> do_billboard_point_eye
doBillboardPointWorld -> do_billboard_point_world
doMethodLater -> do_method_later
drawInFront -> draw_in_front
enableBlend -> enable_blend
encodeToBamStream -> encode_to_bam_stream
exposeJoint -> expose_joint
faceAwayFromViewer -> face_away_from_viewer
faceTowardsViewer -> face_towards_viewer
findAllMatches -> find_all_matches
findAllMaterials -> find_all_materials
findAllPathsTo -> find_all_paths_to
findAllTexcoords -> find_all_texcoords
findAllTextureStages -> find_all_texture_stages
findAllTextures -> find_all_textures
findAllVertexColumns -> find_all_vertex_columns
findMaterial -> find_material
findNetPythonTag -> find_net_python_tag
findNetTag -> find_net_tag
findPathTo -> find_path_to
findTexture -> find_texture
findTextureStage -> find_texture_stage
fixBounds -> fix_bounds
flattenLight -> flatten_light
flattenMedium -> flatten_medium
flattenStrong -> flatten_strong
forceRecomputeBounds -> force_recompute_bounds
freezeJoint -> freeze_joint
getActorInfo -> get_actor_info
getAllAccepting -> get_all_accepting
getAncestor -> get_ancestor
getAncestors -> get_ancestors
getAnimBlends -> get_anim_blends
getAnimControl -> get_anim_control
getAnimControlDict -> get_anim_control_dict
getAnimControls -> get_anim_controls
getAnimFilename -> get_anim_filename
getAnimNames -> get_anim_names
getAntialias -> get_antialias
getAttrib -> get_attrib
getAudioVolume -> get_audio_volume
getBaseFrameRate -> get_base_frame_rate
getBinDrawOrder -> get_bin_draw_order
getBinName -> get_bin_name
getBounds -> get_bounds
getChild -> get_child
getChildren -> get_children
getClassType -> get_class_type
getCollideMask -> get_collide_mask
getColor -> get_color
getColorScale -> get_color_scale
getCommonAncestor -> get_common_ancestor
getCurrentAnim -> get_current_anim
getCurrentFrame -> get_current_frame
getDepthOffset -> get_depth_offset
getDepthTest -> get_depth_test
getDepthWrite -> get_depth_write
getDistance -> get_distance
getDuration -> get_duration
getEffect -> get_effect
getEffects -> get_effects
getErrorType -> get_error_type
getFog -> get_fog
getFrameRate -> get_frame_rate
getFrameTime -> get_frame_time
getGeomNode -> get_geom_node
getH -> get_h
getHiddenAncestor -> get_hidden_ancestor
getHpr -> get_hpr
getInstanceCount -> get_instance_count
getJointTransform -> get_joint_transform
getJointTransformState -> get_joint_transform_state
getJoints -> get_joints
getKey -> get_key
getLOD -> get_lod
getLODIndex -> get_lod_index
getLODNames -> get_lod_names
getLODNode -> get_lod_node
getLogicOp -> get_logic_op
getMat -> get_mat
getMaterial -> get_material
getMaxSearchDepth -> get_max_search_depth
getName -> get_name
getNetAudioVolume -> get_net_audio_volume
getNetPrevTransform -> get_net_prev_transform
getNetPythonTag -> get_net_python_tag
getNetState -> get_net_state
getNetTag -> get_net_tag
getNetTransform -> get_net_transform
getNode -> get_node
getNodes -> get_nodes
getNumChildren -> get_num_children
getNumFrames -> get_num_frames
getNumNodes -> get_num_nodes
getOverlappingJoints -> get_overlapping_joints
getP -> get_p
getParent -> get_parent
getPart -> get_part
getPartBundle -> get_part_bundle
getPartBundleDict -> get_part_bundle_dict
getPartBundles -> get_part_bundles
getPartNames -> get_part_names
getPlayRate -> get_play_rate
getPos -> get_pos
getPosDelta -> get_pos_delta
getPrevTransform -> get_prev_transform
getPythonTag -> get_python_tag
getPythonTagKeys -> get_python_tag_keys
getPythonTags -> get_python_tags
getQuat -> get_quat
getR -> get_r
getRelativePoint -> get_relative_point
getRelativeVector -> get_relative_vector
getRenderMode -> get_render_mode
getRenderModePerspective -> get_render_mode_perspective
getRenderModeThickness -> get_render_mode_thickness
getSa -> get_sa
getSb -> get_sb
getScale -> get_scale
getSg -> get_sg
getShader -> get_shader
getShaderInput -> get_shader_input
getShear -> get_shear
getShxy -> get_shxy
getShxz -> get_shxz
getShyz -> get_shyz
getSort -> get_sort
getSr -> get_sr
getStashedAncestor -> get_stashed_ancestor
getStashedChildren -> get_stashed_children
getState -> get_state
getSubpartsComplete -> get_subparts_complete
getSx -> get_sx
getSy -> get_sy
getSz -> get_sz
getTag -> get_tag
getTagKeys -> get_tag_keys
getTags -> get_tags
getTexGen -> get_tex_gen
getTexHpr -> get_tex_hpr
getTexOffset -> get_tex_offset
getTexPos -> get_tex_pos
getTexProjectorFrom -> get_tex_projector_from
getTexProjectorTo -> get_tex_projector_to
getTexRotate -> get_tex_rotate
getTexScale -> get_tex_scale
getTexScale3d -> get_tex_scale_3d
getTexTransform -> get_tex_transform
getTexture -> get_texture
getTextureSampler -> get_texture_sampler
getTightBounds -> get_tight_bounds
getTop -> get_top
getTopNode -> get_top_node
getTransform -> get_transform
getTransparency -> get_transparency
getTwoSided -> get_two_sided
getX -> get_x
getY -> get_y
getZ -> get_z
hasAntialias -> has_antialias
hasAttrib -> has_attrib
hasAudioVolume -> has_audio_volume
hasBillboard -> has_billboard
hasBin -> has_bin
hasClipPlane -> has_clip_plane
hasClipPlaneOff -> has_clip_plane_off
hasColor -> has_color
hasColorScale -> has_color_scale
hasCompass -> has_compass
hasDepthOffset -> has_depth_offset
hasDepthTest -> has_depth_test
hasDepthWrite -> has_depth_write
hasEffect -> has_effect
hasFog -> has_fog
hasFogOff -> has_fog_off
hasLOD -> has_lod
hasLight -> has_light
hasLightOff -> has_light_off
hasLogicOp -> has_logic_op
hasMat -> has_mat
hasMaterial -> has_material
hasNetPythonTag -> has_net_python_tag
hasNetTag -> has_net_tag
hasOccluder -> has_occluder
hasParent -> has_parent
hasPythonTag -> has_python_tag
hasRenderMode -> has_render_mode
hasScissor -> has_scissor
hasTag -> has_tag
hasTexGen -> has_tex_gen
hasTexProjector -> has_tex_projector
hasTexTransform -> has_tex_transform
hasTexcoord -> has_texcoord
hasTexture -> has_texture
hasTextureOff -> has_texture_off
hasTransparency -> has_transparency
hasTwoSided -> has_two_sided
hasVertexColumn -> has_vertex_column
headsUp -> heads_up
hideAllBounds -> hide_all_bounds
hideBounds -> hide_bounds
hidePart -> hide_part
ignoreAll -> ignore_all
initAnimsOnAllLODs -> init_anims_on_all_lods
instanceTo -> instance_to
instanceUnderNode -> instance_under_node
isAccepting -> is_accepting
isAncestorOf -> is_ancestor_of
isEmpty -> is_empty
isHidden -> is_hidden
isIgnoring -> is_ignoring
isSameGraph -> is_same_graph
isSingleton -> is_singleton
isStashed -> is_stashed
listJoints -> list_joints
listTags -> list_tags
loadAnims -> load_anims
loadAnimsOnAllLODs -> load_anims_on_all_lods
loadModel -> load_model
lookAt -> look_at
makeSubpart -> make_subpart
notFound -> not_found
osdAnimBlends -> osd_anim_blends
postFlatten -> post_flatten
premungeScene -> premunge_scene
prepareScene -> prepare_scene
printAnimBlends -> print_anim_blends
printLOD -> print_lod
projectTexture -> project_texture
releaseJoint -> release_joint
removeAllTasks -> remove_all_tasks
removeAnimControlDict -> remove_anim_control_dict
removeNode -> remove_node
removePart -> remove_part
removeTask -> remove_task
renamePartBundles -> rename_part_bundles
reparentTo -> reparent_to
replaceMaterial -> replace_material
resetLOD -> reset_lod
reverseLs -> reverse_ls
setAllColorScale -> set_all_color_scale
setAlphaScale -> set_alpha_scale
setAntialias -> set_antialias
setAttrib -> set_attrib
setAudioVolume -> set_audio_volume
setAudioVolumeOff -> set_audio_volume_off
setBillboardAxis -> set_billboard_axis
setBillboardPointEye -> set_billboard_point_eye
setBillboardPointWorld -> set_billboard_point_world
setBin -> set_bin
setBlend -> set_blend
setCenter -> set_center
setClipPlane -> set_clip_plane
setClipPlaneOff -> set_clip_plane_off
setCollideMask -> set_collide_mask
setColor -> set_color
setColorOff -> set_color_off
setColorScale -> set_color_scale
setColorScaleOff -> set_color_scale_off
setCompass -> set_compass
setControlEffect -> set_control_effect
setDepthOffset -> set_depth_offset
setDepthTest -> set_depth_test
setDepthWrite -> set_depth_write
setEffect -> set_effect
setEffects -> set_effects
setFluidPos -> set_fluid_pos
setFluidX -> set_fluid_x
setFluidY -> set_fluid_y
setFluidZ -> set_fluid_z
setFog -> set_fog
setFogOff -> set_fog_off
setGeomNode -> set_geom_node
setH -> set_h
setHpr -> set_hpr
setHprScale -> set_hpr_scale
setInstanceCount -> set_instance_count
setLOD -> set_lod
setLODAnimation -> set_lod_animation
setLODNode -> set_lod_node
setLight -> set_light
setLightOff -> set_light_off
setLogicOp -> set_logic_op
setMat -> set_mat
setMaterial -> set_material
setMaterialOff -> set_material_off
setMaxSearchDepth -> set_max_search_depth
setName -> set_name
setOccluder -> set_occluder
setP -> set_p
setPlayRate -> set_play_rate
setPos -> set_pos
setPosHpr -> set_pos_hpr
setPosHprScale -> set_pos_hpr_scale
setPosHprScaleShear -> set_pos_hpr_scale_shear
setPosQuat -> set_pos_quat
setPosQuatScale -> set_pos_quat_scale
setPosQuatScaleShear -> set_pos_quat_scale_shear
setPrevTransform -> set_prev_transform
setPythonTag -> set_python_tag
setQuat -> set_quat
setQuatScale -> set_quat_scale
setR -> set_r
setRenderMode -> set_render_mode
setRenderModeFilled -> set_render_mode_filled
setRenderModeFilledWireframe -> set_render_mode_filled_wireframe
setRenderModePerspective -> set_render_mode_perspective
setRenderModeThickness -> set_render_mode_thickness
setRenderModeWireframe -> set_render_mode_wireframe
setSa -> set_sa
setSb -> set_sb
setScale -> set_scale
setScissor -> set_scissor
setSg -> set_sg
setShader -> set_shader
setShaderAuto -> set_shader_auto
setShaderInput -> set_shader_input
setShaderInputs -> set_shader_inputs
setShaderOff -> set_shader_off
setShear -> set_shear
setShxy -> set_shxy
setShxz -> set_shxz
setShyz -> set_shyz
setSr -> set_sr
setState -> set_state
setSubpartsComplete -> set_subparts_complete
setSx -> set_sx
setSy -> set_sy
setSz -> set_sz
setTag -> set_tag
setTexGen -> set_tex_gen
setTexHpr -> set_tex_hpr
setTexOffset -> set_tex_offset
setTexPos -> set_tex_pos
setTexProjector -> set_tex_projector
setTexRotate -> set_tex_rotate
setTexScale -> set_tex_scale
setTexTransform -> set_tex_transform
setTexture -> set_texture
setTextureOff -> set_texture_off
setTransform -> set_transform
setTransparency -> set_transparency
setTwoSided -> set_two_sided
setX -> set_x
setY -> set_y
setZ -> set_z
showAllBounds -> show_all_bounds
showAllParts -> show_all_parts
showBounds -> show_bounds
showPart -> show_part
showThrough -> show_through
showTightBounds -> show_tight_bounds
stashTo -> stash_to
stopJoint -> stop_joint
unifyTextureStages -> unify_texture_stages
unloadAnims -> unload_anims
unstashAll -> unstash_all
useLOD -> use_lod
verifyComplete -> verify_complete
verifySubpartsComplete -> verify_subparts_complete
waitPending -> wait_pending
writeBamFile -> write_bam_file
writeBamStream -> write_bam_stream
writeBounds -> write_bounds
wrtReparentTo -> wrt_reparent_to

I’ve used this script in the past to make some snake_case aliases, it’s a bit of a hack and I expect it may miss some edge cases but it’s a start:

This is necessary to rename user functions. I found out the panda methods directly from dir () and compared them to each other. Please note this guarantees work in such cases when there is no snake style for the method.

My solution is based not on the presence of capital letters, but on patterns. This is brief.

I counted that panda3d.core has 5883 aliases !!!