Merge lp:~abentley/bzr/unbreak-merge into lp:bzr/2.0
- unbreak-merge
- Merge into 2.0
Status: | Rejected |
---|---|
Rejected by: | Aaron Bentley |
Proposed branch: | lp:~abentley/bzr/unbreak-merge |
Merge into: | lp:bzr/2.0 |
Diff against target: |
54390 lines (+23989/-8597) 483 files modified
.bzrignore (+3/-0) Makefile (+10/-19) NEWS (+993/-13) bzr (+1/-1) bzrlib/__init__.py (+3/-8) bzrlib/_annotator_pyx.pyx (+1/-1) bzrlib/_bencode_pyx.pyx (+10/-2) bzrlib/_btree_serializer_py.py (+14/-8) bzrlib/_btree_serializer_pyx.pyx (+88/-44) bzrlib/_chk_map_py.py (+5/-4) bzrlib/_chk_map_pyx.pyx (+42/-19) bzrlib/_dirstate_helpers_pyx.pyx (+1/-1) bzrlib/_export_c_api.h (+104/-0) bzrlib/_groupcompress_pyx.pyx (+68/-51) bzrlib/_import_c_api.h (+189/-0) bzrlib/_knit_load_data_pyx.pyx (+1/-1) bzrlib/_known_graph_py.py (+91/-2) bzrlib/_known_graph_pyx.pyx (+132/-20) bzrlib/_patiencediff_c.c (+2/-2) bzrlib/_readdir_pyx.pyx (+1/-1) bzrlib/_rio_pyx.pyx (+1/-1) bzrlib/_simple_set_pyx.pxd (+91/-0) bzrlib/_simple_set_pyx.pyx (+592/-0) bzrlib/_static_tuple_c.c (+945/-0) bzrlib/_static_tuple_c.h (+116/-0) bzrlib/_static_tuple_c.pxd (+44/-0) bzrlib/_static_tuple_py.py (+80/-0) bzrlib/_walkdirs_win32.pyx (+1/-1) bzrlib/annotate.py (+2/-1) bzrlib/benchmarks/bench_dirstate.py (+7/-7) bzrlib/bencode.py (+4/-1) bzrlib/branch.py (+30/-17) bzrlib/btree_index.py (+50/-17) bzrlib/bugtracker.py (+20/-17) bzrlib/builtins.py (+974/-864) bzrlib/bundle/__init__.py (+26/-29) bzrlib/bundle/apply_bundle.py (+2/-1) bzrlib/bzrdir.py (+69/-22) bzrlib/check.py (+7/-7) bzrlib/chk_map.py (+147/-36) bzrlib/chk_serializer.py (+4/-3) bzrlib/cleanup.py (+186/-0) bzrlib/cmd_version_info.py (+6/-3) bzrlib/commands.py (+58/-37) bzrlib/commit.py (+138/-149) bzrlib/config.py (+43/-3) bzrlib/conflicts.py (+8/-10) bzrlib/decorators.py (+105/-1) bzrlib/delta.py (+2/-2) bzrlib/diff.py (+70/-20) bzrlib/dirstate.py (+6/-5) bzrlib/doc_generate/autodoc_man.py (+2/-2) bzrlib/doc_generate/autodoc_rstx.py (+43/-59) bzrlib/errors.py (+56/-7) bzrlib/export/dir_exporter.py (+1/-1) bzrlib/export/zip_exporter.py (+3/-2) bzrlib/fetch.py (+19/-6) bzrlib/foreign.py (+51/-13) bzrlib/globbing.py (+41/-4) bzrlib/graph.py (+17/-2) bzrlib/groupcompress.py (+63/-48) bzrlib/help.py (+6/-2) bzrlib/help_topics/__init__.py (+66/-27) bzrlib/help_topics/en/authentication.txt (+2/-4) bzrlib/help_topics/en/configuration.txt (+74/-1) bzrlib/help_topics/en/conflict-types.txt (+26/-3) bzrlib/help_topics/en/content-filters.txt (+2/-2) bzrlib/help_topics/en/debug-flags.txt (+8/-3) bzrlib/help_topics/en/log-formats.txt (+1/-1) bzrlib/help_topics/en/patterns.txt (+3/-0) bzrlib/help_topics/en/rules.txt (+9/-6) bzrlib/help_topics/es/conflicts.txt (+43/-43) bzrlib/hooks.py (+4/-2) bzrlib/index.py (+57/-23) bzrlib/info.py (+2/-1) bzrlib/inventory.py (+66/-51) bzrlib/knit.py (+12/-5) bzrlib/lock.py (+21/-0) bzrlib/lockable_files.py (+2/-2) bzrlib/lockdir.py (+23/-16) bzrlib/log.py (+112/-52) bzrlib/lsprof.py (+55/-26) bzrlib/mail_client.py (+66/-0) bzrlib/merge.py (+462/-276) bzrlib/merge_directive.py (+6/-8) bzrlib/msgeditor.py (+9/-11) bzrlib/mutabletree.py (+41/-34) bzrlib/option.py (+15/-19) bzrlib/osutils.py (+235/-27) bzrlib/patches.py (+1/-1) bzrlib/plugin.py (+90/-26) bzrlib/plugins/launchpad/__init__.py (+45/-33) bzrlib/plugins/launchpad/lp_api.py (+135/-0) bzrlib/plugins/launchpad/lp_directory.py (+3/-9) bzrlib/plugins/launchpad/lp_registration.py (+54/-18) bzrlib/plugins/launchpad/test_lp_api.py (+101/-0) bzrlib/plugins/launchpad/test_lp_directory.py (+145/-2) bzrlib/plugins/launchpad/test_lp_open.py (+16/-4) bzrlib/plugins/news_merge/README (+7/-0) bzrlib/plugins/news_merge/__init__.py (+66/-0) bzrlib/plugins/news_merge/news_merge.py (+94/-0) bzrlib/plugins/news_merge/parser.py (+62/-0) bzrlib/plugins/news_merge/tests/__init__.py (+23/-0) bzrlib/plugins/news_merge/tests/test_news_merge.py (+29/-0) bzrlib/progress.py (+15/-3) bzrlib/push.py (+5/-1) bzrlib/python-compat.h (+11/-0) bzrlib/reconcile.py (+50/-51) bzrlib/reconfigure.py (+1/-3) bzrlib/registry.py (+4/-1) bzrlib/remote.py (+98/-26) bzrlib/repofmt/groupcompress_repo.py (+24/-9) bzrlib/repofmt/pack_repo.py (+104/-56) bzrlib/repofmt/weaverepo.py (+1/-1) bzrlib/repository.py (+65/-30) bzrlib/revision.py (+6/-3) bzrlib/revisionspec.py (+81/-22) bzrlib/revisiontree.py (+5/-2) bzrlib/rio.py (+3/-1) bzrlib/send.py (+2/-3) bzrlib/serializer.py (+6/-2) bzrlib/shelf_ui.py (+120/-27) bzrlib/shellcomplete.py (+1/-1) bzrlib/smart/branch.py (+2/-2) bzrlib/smart/bzrdir.py (+67/-8) bzrlib/smart/client.py (+4/-1) bzrlib/smart/medium.py (+19/-7) bzrlib/smart/message.py (+1/-1) bzrlib/smart/protocol.py (+81/-17) bzrlib/smart/repository.py (+1/-1) bzrlib/smart/request.py (+65/-16) bzrlib/smart/server.py (+139/-33) bzrlib/smart/vfs.py (+10/-1) bzrlib/static_tuple.py (+56/-0) bzrlib/status.py (+17/-11) bzrlib/switch.py (+9/-6) bzrlib/tests/TestUtil.py (+13/-1) bzrlib/tests/__init__.py (+932/-662) bzrlib/tests/blackbox/test_add.py (+1/-16) bzrlib/tests/blackbox/test_annotate.py (+1/-1) bzrlib/tests/blackbox/test_bound_branches.py (+6/-12) bzrlib/tests/blackbox/test_branch.py (+9/-10) bzrlib/tests/blackbox/test_breakin.py (+20/-23) bzrlib/tests/blackbox/test_cat.py (+22/-11) bzrlib/tests/blackbox/test_checkout.py (+2/-10) bzrlib/tests/blackbox/test_commit.py (+51/-5) bzrlib/tests/blackbox/test_debug.py (+2/-3) bzrlib/tests/blackbox/test_diff.py (+7/-7) bzrlib/tests/blackbox/test_dpush.py (+105/-44) bzrlib/tests/blackbox/test_exceptions.py (+67/-13) bzrlib/tests/blackbox/test_export.py (+2/-2) bzrlib/tests/blackbox/test_filesystem_cicp.py (+9/-2) bzrlib/tests/blackbox/test_filtered_view_ops.py (+22/-21) bzrlib/tests/blackbox/test_help.py (+18/-3) bzrlib/tests/blackbox/test_ignore.py (+1/-2) bzrlib/tests/blackbox/test_info.py (+9/-7) bzrlib/tests/blackbox/test_init.py (+4/-3) bzrlib/tests/blackbox/test_locale.py (+9/-6) bzrlib/tests/blackbox/test_log.py (+16/-1) bzrlib/tests/blackbox/test_ls.py (+2/-1) bzrlib/tests/blackbox/test_merge.py (+98/-42) bzrlib/tests/blackbox/test_mv.py (+10/-2) bzrlib/tests/blackbox/test_non_ascii.py (+7/-7) bzrlib/tests/blackbox/test_outside_wt.py (+33/-31) bzrlib/tests/blackbox/test_pull.py (+1/-1) bzrlib/tests/blackbox/test_push.py (+50/-7) bzrlib/tests/blackbox/test_remerge.py (+2/-2) bzrlib/tests/blackbox/test_remove.py (+9/-7) bzrlib/tests/blackbox/test_remove_tree.py (+27/-0) bzrlib/tests/blackbox/test_selftest.py (+13/-16) bzrlib/tests/blackbox/test_send.py (+6/-9) bzrlib/tests/blackbox/test_serve.py (+154/-137) bzrlib/tests/blackbox/test_shared_repository.py (+21/-3) bzrlib/tests/blackbox/test_shelve.py (+24/-1) bzrlib/tests/blackbox/test_split.py (+1/-1) bzrlib/tests/blackbox/test_switch.py (+43/-1) bzrlib/tests/blackbox/test_too_much.py (+8/-25) bzrlib/tests/blackbox/test_uncommit.py (+2/-2) bzrlib/tests/blackbox/test_update.py (+141/-35) bzrlib/tests/blackbox/test_version.py (+20/-0) bzrlib/tests/commands/test_branch.py (+4/-4) bzrlib/tests/commands/test_cat.py (+2/-2) bzrlib/tests/commands/test_checkout.py (+3/-3) bzrlib/tests/commands/test_commit.py (+2/-2) bzrlib/tests/commands/test_init.py (+2/-2) bzrlib/tests/commands/test_init_repository.py (+2/-2) bzrlib/tests/commands/test_merge.py (+2/-2) bzrlib/tests/commands/test_missing.py (+2/-2) bzrlib/tests/commands/test_pull.py (+3/-3) bzrlib/tests/commands/test_push.py (+3/-3) bzrlib/tests/commands/test_update.py (+5/-2) bzrlib/tests/features.py (+11/-19) bzrlib/tests/ftp_server/__init__.py (+3/-3) bzrlib/tests/ftp_server/medusa_based.py (+5/-4) bzrlib/tests/ftp_server/pyftpdlib_based.py (+16/-12) bzrlib/tests/http_server.py (+18/-16) bzrlib/tests/http_utils.py (+4/-5) bzrlib/tests/lock_helpers.py (+2/-0) bzrlib/tests/per_branch/test_hooks.py (+3/-7) bzrlib/tests/per_branch/test_iter_merge_sorted_revisions.py (+23/-1) bzrlib/tests/per_branch/test_locking.py (+2/-2) bzrlib/tests/per_branch/test_parent.py (+3/-3) bzrlib/tests/per_branch/test_permissions.py (+4/-4) bzrlib/tests/per_branch/test_push.py (+4/-5) bzrlib/tests/per_bzrdir/test_bzrdir.py (+42/-14) bzrlib/tests/per_foreign_vcs/__init__.py (+50/-0) bzrlib/tests/per_foreign_vcs/test_branch.py (+151/-0) bzrlib/tests/per_foreign_vcs/test_repository.py (+88/-0) bzrlib/tests/per_interbranch/test_push.py (+1/-2) bzrlib/tests/per_intertree/__init__.py (+3/-2) bzrlib/tests/per_merger.py (+403/-0) bzrlib/tests/per_pack_repository.py (+6/-8) bzrlib/tests/per_repository/test_check.py (+6/-9) bzrlib/tests/per_repository/test_check_reconcile.py (+3/-4) bzrlib/tests/per_repository/test_commit_builder.py (+7/-1) bzrlib/tests/per_repository/test_repository.py (+10/-17) bzrlib/tests/per_repository/test_write_group.py (+1/-1) bzrlib/tests/per_repository_reference/test_check.py (+2/-3) bzrlib/tests/per_transport.py (+6/-2) bzrlib/tests/per_tree/test_get_file_with_stat.py (+4/-2) bzrlib/tests/per_tree/test_inv.py (+1/-1) bzrlib/tests/per_tree/test_iter_search_rules.py (+1/-2) bzrlib/tests/per_tree/test_path_content_summary.py (+28/-5) bzrlib/tests/per_uifactory/__init__.py (+251/-0) bzrlib/tests/per_versionedfile.py (+5/-1) bzrlib/tests/per_workingtree/test_content_filters.py (+1/-1) bzrlib/tests/per_workingtree/test_flush.py (+9/-1) bzrlib/tests/per_workingtree/test_is_ignored.py (+16/-1) bzrlib/tests/per_workingtree/test_locking.py (+11/-2) bzrlib/tests/per_workingtree/test_set_root_id.py (+5/-0) bzrlib/tests/per_workingtree/test_smart_add.py (+8/-2) bzrlib/tests/per_workingtree/test_workingtree.py (+37/-16) bzrlib/tests/script.py (+477/-0) bzrlib/tests/ssl_certs/create_ssls.py (+8/-1) bzrlib/tests/ssl_certs/server.crt (+26/-26) bzrlib/tests/ssl_certs/server.csr (+23/-23) bzrlib/tests/ssl_certs/server_with_pass.key (+50/-50) bzrlib/tests/ssl_certs/server_without_pass.key (+49/-49) bzrlib/tests/stub_sftp.py (+379/-46) bzrlib/tests/test__annotator.py (+4/-31) bzrlib/tests/test__bencode.py (+16/-43) bzrlib/tests/test__chk_map.py (+25/-46) bzrlib/tests/test__chunks_to_lines.py (+11/-29) bzrlib/tests/test__dirstate_helpers.py (+28/-30) bzrlib/tests/test__groupcompress.py (+12/-26) bzrlib/tests/test__known_graph.py (+105/-25) bzrlib/tests/test__rio.py (+3/-30) bzrlib/tests/test__simple_set.py (+381/-0) bzrlib/tests/test__static_tuple.py (+648/-0) bzrlib/tests/test__walkdirs_win32.py (+3/-16) bzrlib/tests/test_branch.py (+17/-0) bzrlib/tests/test_btree_index.py (+39/-39) bzrlib/tests/test_bundle.py (+5/-8) bzrlib/tests/test_bzrdir.py (+14/-6) bzrlib/tests/test_chk_map.py (+50/-77) bzrlib/tests/test_cleanup.py (+278/-0) bzrlib/tests/test_commit.py (+2/-2) bzrlib/tests/test_config.py (+80/-13) bzrlib/tests/test_conflicts.py (+92/-92) bzrlib/tests/test_crash.py (+17/-26) bzrlib/tests/test_decorators.py (+33/-10) bzrlib/tests/test_diff.py (+72/-31) bzrlib/tests/test_directory_service.py (+12/-6) bzrlib/tests/test_errors.py (+39/-2) bzrlib/tests/test_foreign.py (+45/-41) bzrlib/tests/test_globbing.py (+54/-1) bzrlib/tests/test_graph.py (+19/-1) bzrlib/tests/test_groupcompress.py (+132/-28) bzrlib/tests/test_hooks.py (+13/-13) bzrlib/tests/test_http.py (+247/-117) bzrlib/tests/test_http_response.py (+2/-3) bzrlib/tests/test_ignores.py (+5/-1) bzrlib/tests/test_index.py (+37/-2) bzrlib/tests/test_info.py (+13/-17) bzrlib/tests/test_knit.py (+5/-15) bzrlib/tests/test_lockable_files.py (+3/-3) bzrlib/tests/test_lockdir.py (+7/-10) bzrlib/tests/test_log.py (+321/-414) bzrlib/tests/test_lsprof.py (+19/-0) bzrlib/tests/test_mail_client.py (+4/-0) bzrlib/tests/test_merge.py (+209/-162) bzrlib/tests/test_merge_core.py (+75/-32) bzrlib/tests/test_msgeditor.py (+28/-3) bzrlib/tests/test_mutabletree.py (+30/-9) bzrlib/tests/test_osutils.py (+196/-33) bzrlib/tests/test_patches.py (+1/-1) bzrlib/tests/test_permissions.py (+4/-4) bzrlib/tests/test_plugins.py (+133/-65) bzrlib/tests/test_progress.py (+14/-1) bzrlib/tests/test_reconfigure.py (+14/-1) bzrlib/tests/test_registry.py (+63/-6) bzrlib/tests/test_remote.py (+109/-24) bzrlib/tests/test_repository.py (+170/-7) bzrlib/tests/test_revisionspec.py (+44/-19) bzrlib/tests/test_revisiontree.py (+9/-1) bzrlib/tests/test_script.py (+449/-0) bzrlib/tests/test_selftest.py (+380/-299) bzrlib/tests/test_sftp_transport.py (+29/-47) bzrlib/tests/test_shelf.py (+6/-0) bzrlib/tests/test_shelf_ui.py (+116/-10) bzrlib/tests/test_smart.py (+206/-33) bzrlib/tests/test_smart_transport.py (+38/-29) bzrlib/tests/test_source.py (+13/-5) bzrlib/tests/test_ssh_transport.py (+20/-1) bzrlib/tests/test_status.py (+1/-1) bzrlib/tests/test_switch.py (+17/-0) bzrlib/tests/test_trace.py (+27/-17) bzrlib/tests/test_transform.py (+103/-11) bzrlib/tests/test_transport.py (+275/-81) bzrlib/tests/test_transport_log.py (+4/-5) bzrlib/tests/test_ui.py (+165/-132) bzrlib/tests/test_urlutils.py (+6/-2) bzrlib/tests/test_version.py (+16/-2) bzrlib/tests/test_win32utils.py (+170/-37) bzrlib/tests/test_wsgi.py (+55/-7) bzrlib/tests/test_xml.py (+34/-1) bzrlib/tests/transport_util.py (+15/-3) bzrlib/textui.py (+0/-40) bzrlib/trace.py (+43/-30) bzrlib/transform.py (+32/-6) bzrlib/transport/__init__.py (+9/-7) bzrlib/transport/chroot.py (+22/-112) bzrlib/transport/decorator.py (+6/-7) bzrlib/transport/ftp/__init__.py (+22/-34) bzrlib/transport/ftp/_gssapi.py (+11/-41) bzrlib/transport/http/__init__.py (+2/-2) bzrlib/transport/http/_urllib.py (+3/-3) bzrlib/transport/http/_urllib2_wrappers.py (+37/-12) bzrlib/transport/http/wsgi.py (+5/-4) bzrlib/transport/local.py (+3/-6) bzrlib/transport/memory.py (+10/-7) bzrlib/transport/pathfilter.py (+195/-0) bzrlib/transport/sftp.py (+5/-328) bzrlib/transport/ssh.py (+50/-25) bzrlib/tree.py (+7/-4) bzrlib/ui/__init__.py (+118/-101) bzrlib/ui/text.py (+163/-26) bzrlib/upgrade.py (+5/-4) bzrlib/urlutils.py (+10/-4) bzrlib/util/_bencode_py.py (+9/-0) bzrlib/version.py (+6/-3) bzrlib/version_info_formats/format_rio.py (+2/-1) bzrlib/versionedfile.py (+63/-2) bzrlib/win32utils.py (+151/-30) bzrlib/workingtree.py (+56/-26) bzrlib/workingtree_4.py (+13/-23) bzrlib/xml4.py (+6/-4) bzrlib/xml5.py (+5/-3) bzrlib/xml7.py (+4/-3) bzrlib/xml8.py (+10/-7) bzrlib/xml_serializer.py (+8/-3) contrib/debian/default (+20/-0) contrib/debian/init.d (+135/-0) doc/default.css (+4/-4) doc/developers/HACKING.txt (+74/-60) doc/developers/_static/bzr-doc.css (+4/-0) doc/developers/_templates/layout.html (+5/-0) doc/developers/add.txt (+1/-1) doc/developers/api-versioning.txt (+2/-2) doc/developers/apport.txt (+2/-2) doc/developers/authentication-ring.txt (+6/-6) doc/developers/bug-handling.txt (+62/-44) doc/developers/bundles.txt (+1/-1) doc/developers/case-insensitive-file-systems.txt (+1/-1) doc/developers/colocated-branches.txt (+9/-9) doc/developers/commit.txt (+19/-19) doc/developers/container-format.txt (+5/-5) doc/developers/content-filtering.txt (+1/-1) doc/developers/contribution-quickstart.txt (+59/-0) doc/developers/cycle.txt (+56/-58) doc/developers/development-repo.txt (+2/-2) doc/developers/diff.txt (+1/-1) doc/developers/directory-fingerprints.txt (+33/-33) doc/developers/ec2.txt (+16/-16) doc/developers/improved_chk_index.txt (+6/-6) doc/developers/incremental-push-pull.txt (+5/-5) doc/developers/index-plain.txt (+9/-9) doc/developers/index.txt (+13/-2) doc/developers/integration.txt (+2/-2) doc/developers/inventory.txt (+10/-10) doc/developers/last-modified.txt (+6/-6) doc/developers/network-protocol.txt (+18/-16) doc/developers/overview.txt (+5/-5) doc/developers/performance-use-case-analysis.txt (+5/-5) doc/developers/planned-change-integration.txt (+5/-5) doc/developers/planned-performance-changes.txt (+1/-1) doc/developers/plans.txt (+1/-1) doc/developers/plugin-api.txt (+6/-6) doc/developers/ppa.txt (+59/-33) doc/developers/principles.txt (+62/-0) doc/developers/process.txt (+5/-5) doc/developers/profiling.txt (+1/-1) doc/developers/releasing.txt (+103/-56) doc/developers/repository-stream.txt (+1/-1) doc/developers/repository.txt (+8/-8) doc/developers/revert.txt (+1/-1) doc/developers/specifications.txt (+1/-1) doc/developers/status.txt (+2/-2) doc/developers/testing.txt (+147/-16) doc/developers/tortoise-strategy.txt (+9/-9) doc/developers/update.txt (+5/-5) doc/developers/win32_build_setup.txt (+213/-0) doc/en/_templates/index.html (+9/-5) doc/en/admin-guide/advanced.txt (+164/-0) doc/en/admin-guide/backup.txt (+204/-0) doc/en/admin-guide/code-browsing.txt (+128/-0) doc/en/admin-guide/hooks-plugins.txt (+310/-0) doc/en/admin-guide/index-plain.txt (+23/-0) doc/en/admin-guide/index.txt (+25/-114) doc/en/admin-guide/integration.txt (+14/-0) doc/en/admin-guide/introduction.txt (+55/-0) doc/en/admin-guide/migration.txt (+73/-0) doc/en/admin-guide/other-setups.txt (+60/-0) doc/en/admin-guide/security.txt (+116/-0) doc/en/admin-guide/simple-setups.txt (+93/-0) doc/en/admin-guide/upgrade.txt (+77/-0) doc/en/conf.py (+1/-2) doc/en/index.txt (+1/-0) doc/en/mini-tutorial/index.txt (+35/-33) doc/en/tutorials/centralized_workflow.txt (+6/-6) doc/en/tutorials/tutorial.txt (+78/-34) doc/en/tutorials/using_bazaar_with_launchpad.txt (+1/-1) doc/en/user-guide/adv_merging.txt (+23/-0) doc/en/user-guide/branching_a_project.txt (+6/-6) doc/en/user-guide/bug_trackers.txt (+1/-1) doc/en/user-guide/configuring_bazaar.txt (+1/-1) doc/en/user-guide/controlling_registration.txt (+1/-1) doc/en/user-guide/distributed_intro.txt (+1/-1) doc/en/user-guide/getting_help.txt (+9/-7) doc/en/user-guide/hooks.txt (+6/-4) doc/en/user-guide/http_smart_server.txt (+8/-8) doc/en/user-guide/index-plain.txt (+1/-1) doc/en/user-guide/index.txt (+1/-1) doc/en/user-guide/introducing_bazaar.txt (+3/-3) doc/en/user-guide/plugins.txt (+9/-21) doc/en/user-guide/publishing_a_branch.txt (+0/-1) doc/en/user-guide/recording_changes.txt (+1/-1) doc/en/user-guide/resolving_conflicts.txt (+1/-1) doc/en/user-guide/reviewing_changes.txt (+10/-2) doc/en/user-guide/sending_changes.txt (+2/-2) doc/en/user-guide/server.txt (+17/-7) doc/en/user-guide/setting_up_email.txt (+2/-2) doc/en/user-guide/shared_repository_layouts.txt (+22/-22) doc/en/user-guide/shelving_changes.txt (+6/-6) doc/en/user-guide/specifying_revisions.txt (+12/-6) doc/en/user-guide/stacked.txt (+1/-1) doc/en/user-guide/svn_plugin.txt (+3/-5) doc/en/user-guide/undoing_mistakes.txt (+1/-1) doc/en/user-guide/version_info.txt (+1/-1) doc/en/user-guide/web_browsing.txt (+2/-2) doc/en/user-guide/writing_a_plugin.txt (+8/-5) doc/en/user-guide/zen.txt (+1/-1) doc/es/index.txt (+1/-1) doc/es/mini-tutorial/index.txt (+22/-22) doc/es/user-guide/index-plain.txt (+2/-2) doc/es/user-guide/index.txt (+2/-2) doc/es/user-guide/version_info.txt (+17/-17) doc/index.es.txt (+4/-4) doc/index.ru.txt (+7/-7) doc/ja/tutorials/using_bazaar_with_launchpad.txt (+1/-1) doc/ja/upgrade-guide/data_migration.txt (+1/-1) doc/ja/user-guide/entering_commands.txt (+1/-1) doc/ja/user-guide/http_smart_server.txt (+9/-9) doc/ja/user-guide/introducing_bazaar.txt (+2/-2) doc/ja/user-guide/setting_up_email.txt (+2/-2) doc/ja/user-guide/version_info.txt (+3/-3) doc/ja/user-reference/index.txt (+148/-148) doc/ru/index.txt (+4/-4) doc/ru/mini-tutorial/index.txt (+10/-10) doc/ru/tutorials/centralized_workflow.txt (+4/-4) doc/ru/tutorials/tutorial.txt (+2/-2) doc/ru/tutorials/using_bazaar_with_launchpad.txt (+1/-1) doc/ru/user-guide/branching_a_project.txt (+1/-1) doc/ru/user-guide/index-plain.txt (+1/-1) doc/ru/user-guide/index.txt (+1/-1) doc/ru/user-guide/introducing_bazaar.txt (+2/-2) doc/ru/user-guide/specifying_revisions.txt (+1/-1) doc/ru/user-guide/zen.txt (+6/-6) setup.py (+12/-3) tools/packaging/build-packages.sh (+1/-1) tools/packaging/update-changelogs.sh (+12/-7) tools/packaging/update-control.sh (+31/-0) tools/packaging/update-packaging-branches.sh (+14/-6) |
To merge this branch: | bzr merge lp:~abentley/bzr/unbreak-merge |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
John A Meinel | Needs Fixing | ||
Review via email: mp+19556@code.launchpad.net |
Commit message
Description of the change
Aaron Bentley (abentley) wrote : | # |
John A Meinel (jameinel) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Aaron Bentley wrote:
> Aaron Bentley has proposed merging lp:~abentley/bzr/unbreak-merge into lp:bzr/2.0.
>
> Requested reviews:
> bzr-core (bzr-core)
>
>
> Hi all,
>
> This branch fixes merge so that it works when the this_tree is not a
> working tree. The ConfigurableFil
> retrieve the configuration, but it should actually use merger.this_branch.
>
> The test simply shows that generating a preview transform with a RevisionTree
> as the this_tree does not raise an exception.The attached diff has been truncated due to its size.
>
review: needs_fixing
I don't know why you proposed it into 2.0, given that this only exists
in 2.1. I'm guessing you just need to resubmit vs the 2.1 branch.
The discussion seems fine.
John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://
iEYEARECAAYFAkt
MvwAoJHN0QnqkQT
=6RG9
-----END PGP SIGNATURE-----
Aaron Bentley (abentley) wrote : | # |
Robert told me to JFDI, so it's playing in PQM (against 2.1) now.
Preview Diff
1 | === modified file '.bzrignore' | |||
2 | --- .bzrignore 2009-09-09 11:43:10 +0000 | |||
3 | +++ .bzrignore 2010-02-18 00:00:52 +0000 | |||
4 | @@ -58,6 +58,9 @@ | |||
5 | 58 | bzrlib/_known_graph_pyx.c | 58 | bzrlib/_known_graph_pyx.c |
6 | 59 | bzrlib/_readdir_pyx.c | 59 | bzrlib/_readdir_pyx.c |
7 | 60 | bzrlib/_rio_pyx.c | 60 | bzrlib/_rio_pyx.c |
8 | 61 | bzrlib/_simple_set_pyx.c | ||
9 | 62 | bzrlib/_simple_set_pyx.h | ||
10 | 63 | bzrlib/_simple_set_pyx_api.h | ||
11 | 61 | bzrlib/_walkdirs_win32.c | 64 | bzrlib/_walkdirs_win32.c |
12 | 62 | # built extension modules | 65 | # built extension modules |
13 | 63 | bzrlib/_*.dll | 66 | bzrlib/_*.dll |
14 | 64 | 67 | ||
15 | === modified file 'Makefile' | |||
16 | --- Makefile 2009-11-18 04:29:56 +0000 | |||
17 | +++ Makefile 2010-02-18 00:00:52 +0000 | |||
18 | @@ -1,4 +1,4 @@ | |||
20 | 1 | # Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd | 1 | # Copyright (C) 2005-2010 Canonical Ltd |
21 | 2 | # | 2 | # |
22 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
23 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
24 | @@ -40,8 +40,6 @@ | |||
25 | 40 | 40 | ||
26 | 41 | check-nodocs: extensions | 41 | check-nodocs: extensions |
27 | 42 | $(PYTHON) -Werror -O ./bzr selftest -1v $(tests) | 42 | $(PYTHON) -Werror -O ./bzr selftest -1v $(tests) |
28 | 43 | @echo "Running all tests with no locale." | ||
29 | 44 | LC_CTYPE= LANG=C LC_ALL= ./bzr selftest -1v $(tests) 2>&1 | sed -e 's/^/[ascii] /' | ||
30 | 45 | 43 | ||
31 | 46 | # Run Python style checker (apt-get install pyflakes) | 44 | # Run Python style checker (apt-get install pyflakes) |
32 | 47 | # | 45 | # |
33 | @@ -201,7 +199,6 @@ | |||
34 | 201 | 199 | ||
35 | 202 | # translate txt docs to html | 200 | # translate txt docs to html |
36 | 203 | derived_txt_files = \ | 201 | derived_txt_files = \ |
37 | 204 | doc/en/user-reference/bzr_man.txt \ | ||
38 | 205 | doc/en/release-notes/NEWS.txt | 202 | doc/en/release-notes/NEWS.txt |
39 | 206 | txt_all = \ | 203 | txt_all = \ |
40 | 207 | doc/en/tutorials/tutorial.txt \ | 204 | doc/en/tutorials/tutorial.txt \ |
41 | @@ -214,6 +211,7 @@ | |||
42 | 214 | doc/ja/tutorials/centralized_workflow.txt \ | 211 | doc/ja/tutorials/centralized_workflow.txt \ |
43 | 215 | $(wildcard doc/*/mini-tutorial/index.txt) \ | 212 | $(wildcard doc/*/mini-tutorial/index.txt) \ |
44 | 216 | $(wildcard doc/*/user-guide/index-plain.txt) \ | 213 | $(wildcard doc/*/user-guide/index-plain.txt) \ |
45 | 214 | doc/en/admin-guide/index-plain.txt \ | ||
46 | 217 | $(wildcard doc/es/guia-usario/*.txt) \ | 215 | $(wildcard doc/es/guia-usario/*.txt) \ |
47 | 218 | $(derived_txt_files) \ | 216 | $(derived_txt_files) \ |
48 | 219 | doc/en/upgrade-guide/index.txt \ | 217 | doc/en/upgrade-guide/index.txt \ |
49 | @@ -223,7 +221,8 @@ | |||
50 | 223 | doc/en/user-guide/index.txt \ | 221 | doc/en/user-guide/index.txt \ |
51 | 224 | doc/es/user-guide/index.txt \ | 222 | doc/es/user-guide/index.txt \ |
52 | 225 | doc/ja/user-guide/index.txt \ | 223 | doc/ja/user-guide/index.txt \ |
54 | 226 | doc/ru/user-guide/index.txt | 224 | doc/ru/user-guide/index.txt \ |
55 | 225 | doc/en/admin-guide/index.txt | ||
56 | 227 | txt_files = $(filter-out $(txt_nohtml), $(txt_all)) | 226 | txt_files = $(filter-out $(txt_nohtml), $(txt_all)) |
57 | 228 | htm_files = $(patsubst %.txt, %.html, $(txt_files)) | 227 | htm_files = $(patsubst %.txt, %.html, $(txt_files)) |
58 | 229 | 228 | ||
59 | @@ -281,6 +280,9 @@ | |||
60 | 281 | #doc/ru/user-guide/index.html: $(wildcard $(addsuffix /*.txt, doc/ru/user-guide)) | 280 | #doc/ru/user-guide/index.html: $(wildcard $(addsuffix /*.txt, doc/ru/user-guide)) |
61 | 282 | # $(rst2html) --stylesheet=../../default.css $(dir $@)index.txt $@ | 281 | # $(rst2html) --stylesheet=../../default.css $(dir $@)index.txt $@ |
62 | 283 | # | 282 | # |
63 | 283 | doc/en/admin-guide/index-plain.html: $(wildcard $(addsuffix /*.txt, doc/en/admin-guide)) | ||
64 | 284 | $(rst2html) --stylesheet=../../default.css $(dir $@)index-plain.txt $@ | ||
65 | 285 | |||
66 | 284 | doc/developers/%.html: doc/developers/%.txt | 286 | doc/developers/%.html: doc/developers/%.txt |
67 | 285 | $(rst2html) --stylesheet=../default.css $< $@ | 287 | $(rst2html) --stylesheet=../default.css $< $@ |
68 | 286 | 288 | ||
69 | @@ -293,9 +295,6 @@ | |||
70 | 293 | %.html: %.txt | 295 | %.html: %.txt |
71 | 294 | $(rst2html) --stylesheet=../../default.css $< $@ | 296 | $(rst2html) --stylesheet=../../default.css $< $@ |
72 | 295 | 297 | ||
73 | 296 | doc/en/user-reference/bzr_man.txt: $(MAN_DEPENDENCIES) | ||
74 | 297 | $(PYTHON) tools/generate_docs.py -o $@ rstx | ||
75 | 298 | |||
76 | 299 | doc/en/release-notes/NEWS.txt: NEWS | 298 | doc/en/release-notes/NEWS.txt: NEWS |
77 | 300 | $(PYTHON) -c "import shutil; shutil.copyfile('$<', '$@')" | 299 | $(PYTHON) -c "import shutil; shutil.copyfile('$<', '$@')" |
78 | 301 | 300 | ||
79 | @@ -409,7 +408,7 @@ | |||
80 | 409 | 408 | ||
81 | 410 | .PHONY: dist dist-upload-escudero check-dist-tarball | 409 | .PHONY: dist dist-upload-escudero check-dist-tarball |
82 | 411 | 410 | ||
84 | 412 | # build a distribution tarball and zip file. | 411 | # build a distribution source tarball |
85 | 413 | # | 412 | # |
86 | 414 | # this method of copying the pyrex generated files is a bit ugly; it would be | 413 | # this method of copying the pyrex generated files is a bit ugly; it would be |
87 | 415 | # nicer to generate it from distutils. | 414 | # nicer to generate it from distutils. |
88 | @@ -419,15 +418,12 @@ | |||
89 | 419 | expbasedir=`mktemp -t -d tmp_bzr_dist.XXXXXXXXXX` && \ | 418 | expbasedir=`mktemp -t -d tmp_bzr_dist.XXXXXXXXXX` && \ |
90 | 420 | expdir=$$expbasedir/bzr-$$version && \ | 419 | expdir=$$expbasedir/bzr-$$version && \ |
91 | 421 | tarball=$$PWD/../bzr-$$version.tar.gz && \ | 420 | tarball=$$PWD/../bzr-$$version.tar.gz && \ |
92 | 422 | zipball=$$PWD/../bzr-$$version.zip && \ | ||
93 | 423 | $(MAKE) clean && \ | 421 | $(MAKE) clean && \ |
94 | 424 | $(MAKE) && \ | 422 | $(MAKE) && \ |
95 | 425 | bzr export $$expdir && \ | 423 | bzr export $$expdir && \ |
97 | 426 | cp bzrlib/*.c $$expdir/bzrlib/. && \ | 424 | cp bzrlib/*.c bzrlib/*.h $$expdir/bzrlib/. && \ |
98 | 427 | tar cfz $$tarball -C $$expbasedir bzr-$$version && \ | 425 | tar cfz $$tarball -C $$expbasedir bzr-$$version && \ |
99 | 428 | (cd $$expbasedir && zip -r $$zipball bzr-$$version) && \ | ||
100 | 429 | gpg --detach-sign $$tarball && \ | 426 | gpg --detach-sign $$tarball && \ |
101 | 430 | gpg --detach-sign $$zipball && \ | ||
102 | 431 | rm -rf $$expbasedir | 427 | rm -rf $$expbasedir |
103 | 432 | 428 | ||
104 | 433 | # run all tests in a previously built tarball | 429 | # run all tests in a previously built tarball |
105 | @@ -445,15 +441,10 @@ | |||
106 | 445 | dist-upload-escudero: | 441 | dist-upload-escudero: |
107 | 446 | version=`./bzr version --short` && \ | 442 | version=`./bzr version --short` && \ |
108 | 447 | tarball=../bzr-$$version.tar.gz && \ | 443 | tarball=../bzr-$$version.tar.gz && \ |
111 | 448 | zipball=../bzr-$$version.zip && \ | 444 | scp $$tarball $$tarball.sig \ |
110 | 449 | scp $$zipball $$zipball.sig $$tarball $$tarball.sig \ | ||
112 | 450 | escudero.ubuntu.com:/srv/bazaar.canonical.com/www/releases/src \ | 445 | escudero.ubuntu.com:/srv/bazaar.canonical.com/www/releases/src \ |
113 | 451 | && \ | 446 | && \ |
114 | 452 | echo verifying over http... && \ | 447 | echo verifying over http... && \ |
115 | 453 | curl http://bazaar-vcs.org/releases/src/bzr-$$version.zip \ | ||
116 | 454 | | diff -s - $$zipball && \ | ||
117 | 455 | curl http://bazaar-vcs.org/releases/src/bzr-$$version.zip.sig \ | ||
118 | 456 | | diff -s - $$zipball.sig | ||
119 | 457 | curl http://bazaar-vcs.org/releases/src/bzr-$$version.tar.gz \ | 448 | curl http://bazaar-vcs.org/releases/src/bzr-$$version.tar.gz \ |
120 | 458 | | diff -s - $$tarball && \ | 449 | | diff -s - $$tarball && \ |
121 | 459 | curl http://bazaar-vcs.org/releases/src/bzr-$$version.tar.gz.sig \ | 450 | curl http://bazaar-vcs.org/releases/src/bzr-$$version.tar.gz.sig \ |
122 | 460 | 451 | ||
123 | === modified file 'NEWS' | |||
124 | --- NEWS 2010-02-12 06:00:33 +0000 | |||
125 | +++ NEWS 2010-02-18 00:00:52 +0000 | |||
126 | @@ -5,6 +5,348 @@ | |||
127 | 5 | .. contents:: List of Releases | 5 | .. contents:: List of Releases |
128 | 6 | :depth: 1 | 6 | :depth: 1 |
129 | 7 | 7 | ||
130 | 8 | |||
131 | 9 | bzr 2.1.1 | ||
132 | 10 | ######### | ||
133 | 11 | |||
134 | 12 | :2.1.1: not released yet | ||
135 | 13 | |||
136 | 14 | Bug Fixes | ||
137 | 15 | ********* | ||
138 | 16 | |||
139 | 17 | * Register SIGWINCH handler only when creating a ``TextUIFactory``; avoids | ||
140 | 18 | problems importing bzrlib from a non-main thread. | ||
141 | 19 | (Elliot Murphy, #521989) | ||
142 | 20 | |||
143 | 21 | * Standardize the error handling when creating a new ``StaticTuple`` | ||
144 | 22 | (problems will raise TypeError). (Matt Nordhoff, #457979) | ||
145 | 23 | |||
146 | 24 | * Merge correctly when this_tree is not a WorkingTree. (Aaron Bentley) | ||
147 | 25 | |||
148 | 26 | |||
149 | 27 | bzr 2.1.0 | ||
150 | 28 | ######### | ||
151 | 29 | |||
152 | 30 | :Codename: Strasbourg | ||
153 | 31 | :2.1.0: 2010-02-11 | ||
154 | 32 | |||
155 | 33 | This release marks our second long-term-stable series. The Bazaar team | ||
156 | 34 | has decided that we will continue to make bugfix-only 2.0.x and 2.1.x | ||
157 | 35 | releases, along with 2.2 development releases. | ||
158 | 36 | |||
159 | 37 | This is a fairly incremental update, focusing on polish and bugfixing. | ||
160 | 38 | There are no changes for supported disk formats. Key updates include | ||
161 | 39 | reduced memory consumption for many operations, a new per-file merge | ||
162 | 40 | hook, ignore patterns can now include '!' to exclude files, globbing | ||
163 | 41 | support for all commands on Windows, and support for addressing home | ||
164 | 42 | directories via ``bzr+ssh://host/~/`` syntax. | ||
165 | 43 | |||
166 | 44 | Users are encouraged to upgrade from the 2.0 stable series. | ||
167 | 45 | |||
168 | 46 | Bug Fixes | ||
169 | 47 | ********* | ||
170 | 48 | |||
171 | 49 | * Don't require testtools to use sftp. | ||
172 | 50 | (Vincent Ladeuil, #516183) | ||
173 | 51 | |||
174 | 52 | * Fix "AttributeError in Inter1and2Helper" during fetch. | ||
175 | 53 | (Martin Pool, #513432) | ||
176 | 54 | |||
177 | 55 | * Ignore ``KeyError`` from ``remove_index`` during ``_abort_write_group`` | ||
178 | 56 | in a pack repository, which can happen harmlessly if the abort occurs during | ||
179 | 57 | finishing the write group. Also use ``bzrlib.cleanup`` so that any | ||
180 | 58 | other errors that occur while aborting the individual packs won't be | ||
181 | 59 | hidden by secondary failures when removing the corresponding indices. | ||
182 | 60 | (Andrew Bennetts, #423015) | ||
183 | 61 | |||
184 | 62 | * Using the ``bzrlib.chk_map`` module from within multiple threads at the | ||
185 | 63 | same time was broken due to race conditions with a module level page | ||
186 | 64 | cache. This shows up as a KeyError in the ``bzrlib.lru_cache`` code with | ||
187 | 65 | ``bzrlib.chk_map`` in the backtrace, and can be triggered without using | ||
188 | 66 | the same high level objects such as ``bzrlib.repository.Repository`` | ||
189 | 67 | from different threads. chk_map now uses a thread local cache which may | ||
190 | 68 | increase memory pressure on processes using threads. | ||
191 | 69 | (Robert Collins, John Arbash Meinel, #514090) | ||
192 | 70 | |||
193 | 71 | * The new ``merge_file_content`` should now be ok with tests to avoid | ||
194 | 72 | regressions. | ||
195 | 73 | (Vincent Ladeuil, #515597) | ||
196 | 74 | |||
197 | 75 | Internals | ||
198 | 76 | ********* | ||
199 | 77 | |||
200 | 78 | * Use ``bzrlib.cleanup`` rather than less robust ``try``/``finally`` | ||
201 | 79 | blocks in several places in ``bzrlib.merge``. This avoids masking prior | ||
202 | 80 | errors when errors like ``ImmortalPendingDeletion`` occur during cleanup | ||
203 | 81 | in ``do_merge``. | ||
204 | 82 | (Andrew Bennetts, #517275) | ||
205 | 83 | |||
206 | 84 | API Changes | ||
207 | 85 | *********** | ||
208 | 86 | |||
209 | 87 | * The ``remove_index`` method of | ||
210 | 88 | ``bzrlib.repofmt.pack_repo.AggregateIndex`` no longer takes a ``pack`` | ||
211 | 89 | argument. This argument was always ignored. | ||
212 | 90 | (Andrew Bennetts, #423015) | ||
213 | 91 | |||
214 | 92 | bzr 2.1.0rc2 | ||
215 | 93 | ############ | ||
216 | 94 | |||
217 | 95 | :Codename: after the bubbles | ||
218 | 96 | :2.1.0rc2: 2010-01-29 | ||
219 | 97 | |||
220 | 98 | This is a quick-turn-around to update a small issue with our new per-file | ||
221 | 99 | merge hook. We expect no major changes from this to the final 2.1.0. | ||
222 | 100 | |||
223 | 101 | API Changes | ||
224 | 102 | *********** | ||
225 | 103 | |||
226 | 104 | * The new ``merge_file_content`` hook point has been altered to provide a | ||
227 | 105 | better API where state for extensions can be stored rather than the | ||
228 | 106 | too-simple function based approach. This fixes a performance regression | ||
229 | 107 | where branch configuration would be parsed per-file during merge. As | ||
230 | 108 | part of this the included news_merger has been refactored into a base | ||
231 | 109 | helper class ``bzrlib.merge.ConfigurableFileMerger``. | ||
232 | 110 | (Robert Collins, John Arbash Meinel, #513822) | ||
233 | 111 | |||
234 | 112 | |||
235 | 113 | bzr 2.1.0rc1 | ||
236 | 114 | ############ | ||
237 | 115 | |||
238 | 116 | :Codename: the 'new' stable | ||
239 | 117 | :2.1.0rc1: 2009-01-21 | ||
240 | 118 | |||
241 | 119 | This is the first stable release candidate for Bazaar's 2.1 series. From | ||
242 | 120 | this point onwards, the 2.1 series will be considered stable (as the 2.0 | ||
243 | 121 | series) and only bugfixes are expected to be incorporated. The dozen or so | ||
244 | 122 | bugfixes in the 2.0.4 release are also included in this release (along | ||
245 | 123 | with more than 15 more bugfixes). Some of the interesting features are | ||
246 | 124 | support for per-file merge hooks, ``bzr unshelve --preview``, support | ||
247 | 125 | for using ! in ignore files to exclude files from being ignored, a small | ||
248 | 126 | memory leak was squashed, and many ``ObjectNotLocked`` errors were fixed. | ||
249 | 127 | This looks to be a very good start for a new stable series. | ||
250 | 128 | |||
251 | 129 | |||
252 | 130 | New Features | ||
253 | 131 | ************ | ||
254 | 132 | |||
255 | 133 | * Add bug information to log output when available. | ||
256 | 134 | (Neil Martinsen-Burrell, Guillermo Gonzalez, #251729) | ||
257 | 135 | |||
258 | 136 | * Added ``merge_file_content`` hook point to ``Merger``, allowing plugins | ||
259 | 137 | to register custom merge logic, e.g. to provide smarter merging for | ||
260 | 138 | particular files. | ||
261 | 139 | |||
262 | 140 | * Bazaar now includes the ``news_merge`` plugin. It is disabled by | ||
263 | 141 | default, to enable it add a ``news_merge_files`` option to your | ||
264 | 142 | configuration. Consult ``bzr help news_merge`` for more information. | ||
265 | 143 | (Andrew Bennetts) | ||
266 | 144 | |||
267 | 145 | * ``bzr branch`` now takes a ``--bind`` option. This lets you | ||
268 | 146 | branch and bind all in one command. (Ian Clatworthy) | ||
269 | 147 | |||
270 | 148 | * ``bzr switch`` now takes a ``--revision`` option, to allow switching to | ||
271 | 149 | a specific revision of a branch. (Daniel Watkins, #183559) | ||
272 | 150 | |||
273 | 151 | * ``bzr unshelve --preview`` can now be used to show how a patch on the | ||
274 | 152 | shelf would be applied to the working tree. | ||
275 | 153 | (Guilherme Salgado, #308122) | ||
276 | 154 | |||
277 | 155 | * ``bzr update`` now takes a ``--revision`` argument. This lets you | ||
278 | 156 | change the revision of the working tree to any revision in the | ||
279 | 157 | ancestry of the current or master branch. (Matthieu Moy, Mark Hammond, | ||
280 | 158 | Martin Pool, #45719) | ||
281 | 159 | |||
282 | 160 | * ``-Dbytes`` can now be used to display the total number of bytes | ||
283 | 161 | transferred for the current command. This information is always logged | ||
284 | 162 | to ``.bzr.log`` for later inspection. (John Arbash Meinel) | ||
285 | 163 | |||
286 | 164 | * New ignore patterns. Patterns prefixed with '!' are exceptions to | ||
287 | 165 | ignore patterns and take precedence over regular ignores. Such | ||
288 | 166 | exceptions are used to specify files that should be versioned which | ||
289 | 167 | would otherwise be ignored. Patterns prefixed with '!!' act as regular | ||
290 | 168 | ignore patterns, but have highest precedence, even over the '!' | ||
291 | 169 | exception patterns. (John Whitley, #428031) | ||
292 | 170 | |||
293 | 171 | * The ``supress_warnings`` configuration option has been introduced to disable | ||
294 | 172 | various warnings (it currently only supports the ``format_deprecation`` | ||
295 | 173 | warning). The new option can be set in any of the following locations: | ||
296 | 174 | ``bazaar.conf``, ``locations.conf`` and/or ``branch.conf``. | ||
297 | 175 | (Ted Gould, Matthew Fuller, Vincent Ladeuil) | ||
298 | 176 | |||
299 | 177 | Bug Fixes | ||
300 | 178 | ********* | ||
301 | 179 | |||
302 | 180 | * Always show a message if an OS error occurs while trying to run a | ||
303 | 181 | user-specified commit message editor. | ||
304 | 182 | (Martin Pool, #504842) | ||
305 | 183 | |||
306 | 184 | * ``bzr diff`` will now use the epoch when it is unable to determine | ||
307 | 185 | the timestamp of a file, if the revision it was introduced in is a | ||
308 | 186 | ghost. (Jelmer Vernooij, #295611) | ||
309 | 187 | |||
310 | 188 | * ``bzr switch -b`` can now create branches that are located using directory | ||
311 | 189 | services such as ``lp:``, even when the branch name doesn't contain a | ||
312 | 190 | '/'. (Neil Martinsen-Burrell, #495263) | ||
313 | 191 | |||
314 | 192 | * ``bzr unshelve`` has improved messages about what it is doing. | ||
315 | 193 | (Neil Martinsen-Burrell, #496917) | ||
316 | 194 | |||
317 | 195 | * Concurrent autopacking is more resilient to already-renamed pack files. | ||
318 | 196 | If we find that a file we are about to obsolete is already obsoleted, we | ||
319 | 197 | do not try to rename it, and we leave the file in ``obsolete_packs``. | ||
320 | 198 | The code is also fault tolerant if a file goes missing, assuming that | ||
321 | 199 | another process already removed the file. | ||
322 | 200 | (John Arbash Meinel, Gareth White, #507557) | ||
323 | 201 | |||
324 | 202 | * Fix "Too many concurrent requests" in reconcile when network connection | ||
325 | 203 | fails. (Andrew Bennetts, #503878) | ||
326 | 204 | |||
327 | 205 | * Fixed a side effect mutation of ``RemoteBzrDirFormat._network_name`` | ||
328 | 206 | that caused some tests to fail when run in a non-default order. | ||
329 | 207 | Probably no user impact. (Martin Pool, #504102) | ||
330 | 208 | |||
331 | 209 | * Fixed ``ObjectNotLocked`` error in ``bzr cat -rbranch:../foo FILE``. | ||
332 | 210 | (Andrew Bennetts, #506274) | ||
333 | 211 | |||
334 | 212 | * FTP transports support Unicode paths by encoding/decoding them as utf8. | ||
335 | 213 | (Vincent Ladeuil, #472161) | ||
336 | 214 | |||
337 | 215 | * Listen to the SIGWINCH signal to update the terminal width. | ||
338 | 216 | (Vincent Ladeuil, #316357) | ||
339 | 217 | |||
340 | 218 | * Progress bars are now hidden when ``--quiet`` is given. | ||
341 | 219 | (Martin Pool, #320035) | ||
342 | 220 | |||
343 | 221 | * ``SilentUIFactory`` now supports ``make_output_stream`` and discards | ||
344 | 222 | whatever is written to it. This un-breaks some plugin tests that | ||
345 | 223 | depended on this behaviour. | ||
346 | 224 | (Martin Pool, #499757) | ||
347 | 225 | |||
348 | 226 | * When operations update the working tree, all affected files should end | ||
349 | 227 | up with the same mtime. (eg. when versioning a generated file, if you | ||
350 | 228 | update the source and the generated file together, the generated file | ||
351 | 229 | should appear up-to-date.) | ||
352 | 230 | (John Arbash Meinel, Martin <gzlist>, #488724) | ||
353 | 231 | |||
354 | 232 | Improvements | ||
355 | 233 | ************ | ||
356 | 234 | |||
357 | 235 | * Added ``add_cleanup`` and ``cleanup_now`` to ``bzrlib.command.Command``. | ||
358 | 236 | All the builtin commands now use ``add_cleanup`` rather than | ||
359 | 237 | ``try``/``finally`` blocks where applicable as it is simpler and more | ||
360 | 238 | robust. (Andrew Bennetts) | ||
361 | 239 | |||
362 | 240 | * All except a small number of storage formats are now hidden, making | ||
363 | 241 | the help for numerous commands far more digestible. (Ian Clatworthy) | ||
364 | 242 | |||
365 | 243 | * Attempts to open a shared repository as a branch (e.g. ``bzr branch | ||
366 | 244 | path/to/repo``) will now include "location is a repository" as a hint in | ||
367 | 245 | the error message. (Brian de Alwis, Andrew Bennetts, #440952) | ||
368 | 246 | |||
369 | 247 | * Push will now inform the user when they are trying to push to a foreign | ||
370 | 248 | VCS for which roundtripping is not supported, and will suggest them to | ||
371 | 249 | use dpush. (Jelmer Vernooij) | ||
372 | 250 | |||
373 | 251 | * The version of bzr being run is now written to the log file. | ||
374 | 252 | (__monty__, #257170) | ||
375 | 253 | |||
376 | 254 | * Transport network activity indicator is shown more of the time when | ||
377 | 255 | Bazaar is doing network IO. | ||
378 | 256 | (Martin Pool) | ||
379 | 257 | |||
380 | 258 | Documentation | ||
381 | 259 | ************* | ||
382 | 260 | |||
383 | 261 | * Add documentation on creating merges with more than one parent. | ||
384 | 262 | (Neil Martinsen-Burrell, #481526) | ||
385 | 263 | |||
386 | 264 | * Better explain the --uncommitted option of merge. | ||
387 | 265 | (Neil Martinsen-Burrell, #505088) | ||
388 | 266 | |||
389 | 267 | * Improve discussion of pending merges in the documentation for | ||
390 | 268 | ``revert``. (Neil Martinsen-Burrell, #505093) | ||
391 | 269 | |||
392 | 270 | * Improved help for ``bzr send``. | ||
393 | 271 | (Martin Pool, Bojan Nikolic) | ||
394 | 272 | |||
395 | 273 | * There is a System Administrator's Guide in ``doc/en/admin-guide``, | ||
396 | 274 | including discussions of installation, relevant plugins, security and | ||
397 | 275 | backup. (Neil Martinsen-Burrell) | ||
398 | 276 | |||
399 | 277 | * The ``conflicts`` help topic has been renamed to ``conflict-types``. | ||
400 | 278 | (Ian Clatworthy) | ||
401 | 279 | |||
402 | 280 | * The User Reference is now presented as a series of topics. | ||
403 | 281 | Many of the included topics have link and format tweaks applied. | ||
404 | 282 | (Ian Clatworthy) | ||
405 | 283 | |||
406 | 284 | API Changes | ||
407 | 285 | *********** | ||
408 | 286 | |||
409 | 287 | * Added ``cachedproperty`` decorator to ``bzrlib.decorators``. | ||
410 | 288 | (Andrew Bennetts) | ||
411 | 289 | |||
412 | 290 | * Many test features were renamed from ``FooFeature`` to ``foo_feature`` | ||
413 | 291 | to be consistent with instances being lower case and classes being | ||
414 | 292 | CamelCase. For the features that were more likely to be used, we added a | ||
415 | 293 | deprecation thunk, but not all. (John Arbash Meinel) | ||
416 | 294 | |||
417 | 295 | * Merger classes (such as ``Merge3Merger``) now expect a ``this_branch`` | ||
418 | 296 | parameter in their constructors, and provide ``this_branch`` as an | ||
419 | 297 | attribute. (Andrew Bennetts) | ||
420 | 298 | |||
421 | 299 | * The Branch hooks pre_change_branch_tip no longer masks exceptions raised | ||
422 | 300 | by plugins - the original exceptions are now preserved. (Robert Collins) | ||
423 | 301 | |||
424 | 302 | * The Transport ``Server.tearDown`` method is now renamed to | ||
425 | 303 | ``stop_server`` and ``setUp`` to ``start_server`` for consistency with | ||
426 | 304 | our normal naming pattern, and to avoid confusion with Python's | ||
427 | 305 | ``TestCase.tearDown``. (Martin Pool) | ||
428 | 306 | |||
429 | 307 | * ``WorkingTree.update`` implementations must now accept a ``revision`` | ||
430 | 308 | parameter. | ||
431 | 309 | |||
432 | 310 | Internals | ||
433 | 311 | ********* | ||
434 | 312 | |||
435 | 313 | * Added ``BzrDir.open_branchV3`` smart server request, which can receive | ||
436 | 314 | a string of details (such as "location is a repository") as part of a | ||
437 | 315 | ``nobranch`` response. (Andrew Bennetts, #440952) | ||
438 | 316 | |||
439 | 317 | * New helper osutils.UnicodeOrBytesToBytesWriter which encodes unicode | ||
440 | 318 | objects but passes str objects straight through. This is used for | ||
441 | 319 | selftest but may be useful for diff and other operations that generate | ||
442 | 320 | mixed output. (Robert Collins) | ||
443 | 321 | |||
444 | 322 | * New exception ``NoRoundtrippingSupport``, for use by foreign branch | ||
445 | 323 | plugins. (Jelmer Vernooij) | ||
446 | 324 | |||
447 | 325 | Testing | ||
448 | 326 | ******* | ||
449 | 327 | |||
450 | 328 | * ``bzrlib.tests.permute_for_extension`` is a helper that simplifies | ||
451 | 329 | running all tests in the current module, once against a pure python | ||
452 | 330 | implementation, and once against an extension (pyrex/C) implementation. | ||
453 | 331 | It can be used to dramatically simplify the implementation of | ||
454 | 332 | ``load_tests``. (John Arbash Meinel) | ||
455 | 333 | |||
456 | 334 | * ``bzrlib.tests.TestCase`` now subclasses ``testtools.testcase.TestCase``. | ||
457 | 335 | This permits features in testtools such as getUniqueInteger and | ||
458 | 336 | getUniqueString to be used. Because of this, testtools version 0.9.2 or | ||
459 | 337 | newer is now a dependency to run bzr selftest. Running with versions of | ||
460 | 338 | testtools less than 0.9.2 will cause bzr to error while loading the test | ||
461 | 339 | suite. (Robert Collins) | ||
462 | 340 | |||
463 | 341 | * Shell-like tests now support the command "mv" for moving files. The | ||
464 | 342 | syntax for ``mv file1 file2``, ``mv dir1 dir2`` and ``mv file dir`` is | ||
465 | 343 | supported. (Neil Martinsen-Burrell) | ||
466 | 344 | |||
467 | 345 | * The test progress bar no longer distinguishes tests that 'errored' from | ||
468 | 346 | tests that 'failed' - they're all just failures. | ||
469 | 347 | (Martin Pool) | ||
470 | 348 | |||
471 | 349 | |||
472 | 8 | bzr 2.0.5 (not released yet) | 350 | bzr 2.0.5 (not released yet) |
473 | 9 | ############################ | 351 | ############################ |
474 | 10 | 352 | ||
475 | @@ -111,6 +453,190 @@ | |||
476 | 111 | bug #495023. (John Arbash Meinel) | 453 | bug #495023. (John Arbash Meinel) |
477 | 112 | 454 | ||
478 | 113 | 455 | ||
479 | 456 | bzr 2.1.0b4 | ||
480 | 457 | ########### | ||
481 | 458 | |||
482 | 459 | :Codename: san francisco airport | ||
483 | 460 | :2.1.0b4: 2009-12-14 | ||
484 | 461 | |||
485 | 462 | The fourth beta release in the 2.1 series brings with it a significant | ||
486 | 463 | number of bugfixes (~20). The test suite is once again (finally) "green" | ||
487 | 464 | on Windows, and should remain that way for future releases. There are a | ||
488 | 465 | few performance related updates (faster upgrade and log), and several UI | ||
489 | 466 | tweaks. There has also been a significant number of tweaks to the runtime | ||
490 | 467 | documentation. 2.1.0b4 include everything from the 2.0.3 release. | ||
491 | 468 | |||
492 | 469 | |||
493 | 470 | Compatibility Breaks | ||
494 | 471 | ******************** | ||
495 | 472 | |||
496 | 473 | * The BZR_SSH environmental variable may now be set to the path of a secure | ||
497 | 474 | shell client. If currently set to the value ``ssh`` it will now guess the | ||
498 | 475 | vendor of the program with that name, to restore the old behaviour that | ||
499 | 476 | indicated the SSH Corporation client use ``sshcorp`` instead as the magic | ||
500 | 477 | string. (Martin <gzlist@googlemail.com>, #176292) | ||
501 | 478 | |||
502 | 479 | New Features | ||
503 | 480 | ************ | ||
504 | 481 | |||
505 | 482 | * ``bzr commit`` now has a ``--commit-time`` option. | ||
506 | 483 | (Alexander Sack, #459276) | ||
507 | 484 | |||
508 | 485 | * ``-Dhpss`` now increases logging done when run on the bzr server, | ||
509 | 486 | similarly to how it works on the client. (John Arbash Meinel) | ||
510 | 487 | |||
511 | 488 | * New option ``bzr unshelve --keep`` applies the changes and leaves them | ||
512 | 489 | on the shelf. (Martin Pool, Oscar Fuentes, #492091) | ||
513 | 490 | |||
514 | 491 | * The ``BZR_COLUMNS`` envrionment variable can be set to force bzr to | ||
515 | 492 | respect a given terminal width. This can be useful when output is | ||
516 | 493 | redirected or in obscure cases where the default value is not | ||
517 | 494 | appropriate. Pagers can use it to get a better control of the line | ||
518 | 495 | lengths. | ||
519 | 496 | (Vincent Ladeuil) | ||
520 | 497 | |||
521 | 498 | * The new command ``bzr lp-mirror`` will request that Launchpad update its | ||
522 | 499 | mirror of a local branch. This command will only function if launchpadlib | ||
523 | 500 | is installed. | ||
524 | 501 | (Jonathan Lange) | ||
525 | 502 | |||
526 | 503 | |||
527 | 504 | Bug Fixes | ||
528 | 505 | ********* | ||
529 | 506 | |||
530 | 507 | * After renaming a file, the dirstate could accidentally reference | ||
531 | 508 | ``source\\path`` rather than ``source/path`` on Windows. This might be a | ||
532 | 509 | source of some dirstate-related failures. (John Arbash Meinel) | ||
533 | 510 | |||
534 | 511 | * ``bzr commit`` now detects commit messages that looks like file names | ||
535 | 512 | and issues a warning. | ||
536 | 513 | (Gioele Barabucci, #73073) | ||
537 | 514 | |||
538 | 515 | * ``bzr ignore /`` no longer causes an IndexError. (Gorder Tyler, #456036) | ||
539 | 516 | |||
540 | 517 | * ``bzr log -n0 -rN`` should not return revisions beyond its merged revisions. | ||
541 | 518 | (#325618, #484109, Marius Kruger) | ||
542 | 519 | |||
543 | 520 | * ``bzr merge --weave`` and ``--lca`` will now create ``.BASE`` files for | ||
544 | 521 | files with conflicts (similar to ``--merge3``). The contents of the file | ||
545 | 522 | is a synthesis of all bases used for the merge. | ||
546 | 523 | (John Arbash Meinel, #40412) | ||
547 | 524 | |||
548 | 525 | * ``bzr mv --quiet`` really is quiet now. (Gordon Tyler, #271790) | ||
549 | 526 | |||
550 | 527 | * ``bzr serve`` is more clear about the risk of supplying --allow-writes. | ||
551 | 528 | (Robert Collins, #84659) | ||
552 | 529 | |||
553 | 530 | * ``bzr serve --quiet`` really is quiet now. (Gordon Tyler, #252834) | ||
554 | 531 | |||
555 | 532 | * Fix bug with redirected URLs over authenticated HTTP. | ||
556 | 533 | (Glen Mailer, Neil Martinsen-Burrell, Vincent Ladeuil, #395714) | ||
557 | 534 | |||
558 | 535 | * Interactive merge doesn't leave branch locks behind. (Aaron Bentley) | ||
559 | 536 | |||
560 | 537 | * Lots of bugfixes for the test suite on Windows. We should once again | ||
561 | 538 | have a test suite with no failures on Windows. (John Arbash Meinel) | ||
562 | 539 | |||
563 | 540 | * ``osutils.terminal_width()`` obeys the BZR_COLUMNS environment | ||
564 | 541 | variable but returns None if the terminal is not a tty (when output is | ||
565 | 542 | redirected for example). Also fixes its usage under OSes that doesn't | ||
566 | 543 | provide termios.TIOCGWINSZ. Make sure the corresponding tests runs on | ||
567 | 544 | windows too. | ||
568 | 545 | (Joke de Buhr, Vincent Ladeuil, #353370, #62539) | ||
569 | 546 | (John Arbash Meinel, Vincent Ladeuil, #492561) | ||
570 | 547 | |||
571 | 548 | * Terminate ssh subprocesses when no references to them remain, fixing | ||
572 | 549 | subprocess and file descriptor leaks. (Andrew Bennetts, #426662) | ||
573 | 550 | |||
574 | 551 | * The ``--hardlink`` option of ``bzr branch`` and ``bzr checkout`` now | ||
575 | 552 | works for 2a format trees. Only files unaffected by content filters | ||
576 | 553 | will be hardlinked. (Andrew Bennetts, #408193) | ||
577 | 554 | |||
578 | 555 | * The new glob expansion on Windows would replace all ``\`` characters | ||
579 | 556 | with ``/`` even if it there wasn't a glob to expand, the arg was quoted, | ||
580 | 557 | etc. Now only change slashes if there is something being glob expanded. | ||
581 | 558 | (John Arbash Meinel, #485771) | ||
582 | 559 | |||
583 | 560 | * Use our faster ``KnownGraph.heads()`` functionality when computing the | ||
584 | 561 | new rich-root heads. This can cut a conversion time in half (mysql from | ||
585 | 562 | 13.5h => 6.2h) (John Arbash Meinel, #487632) | ||
586 | 563 | |||
587 | 564 | * When launching a external diff tool via bzr diff --using, temporary files | ||
588 | 565 | are no longer created, rather, the path to the file in the working tree is | ||
589 | 566 | passed to the external diff tool. This allows the file to be edited if the | ||
590 | 567 | diff tool provides for this. (Gary van der Merwe, #490738) | ||
591 | 568 | |||
592 | 569 | * The launchpad-open command can now be used from a subdirectory of a | ||
593 | 570 | branch, not just from the root of the branch. | ||
594 | 571 | (Neil Martinsen-Burrell, #489102) | ||
595 | 572 | |||
596 | 573 | |||
597 | 574 | Improvements | ||
598 | 575 | ************ | ||
599 | 576 | |||
600 | 577 | * ``bzr log`` is now faster. (Ian Clatworthy) | ||
601 | 578 | |||
602 | 579 | * ``bzr update`` provides feedback on which branch it is up to date with. | ||
603 | 580 | (Neil Martinsen-Burrell) | ||
604 | 581 | |||
605 | 582 | * ``bzr upgrade`` from pre-2a to 2a can be significantly faster (4x). | ||
606 | 583 | For details see the xml8 patch and heads() improvements. | ||
607 | 584 | (John Arbash Meinel) | ||
608 | 585 | |||
609 | 586 | * ``bzrlib.urlutils.local_path_from_url`` now accepts | ||
610 | 587 | 'file://localhost/' as well as 'file:///' URLs on POSIX. (Michael | ||
611 | 588 | Hudson) | ||
612 | 589 | |||
613 | 590 | * The progress bar now shows only a spinner and per-operation counts, | ||
614 | 591 | not an overall progress bar. The previous bar was often not correlated | ||
615 | 592 | with real overall operation progress, either because the operations take | ||
616 | 593 | nonlinear time, or because at the start of the operation Bazaar couldn't | ||
617 | 594 | estimate how much work there was to do. (Martin Pool) | ||
618 | 595 | |||
619 | 596 | Documentation | ||
620 | 597 | ************* | ||
621 | 598 | |||
622 | 599 | * Lots of documentation tweaks for inline help topics and command help | ||
623 | 600 | information. | ||
624 | 601 | |||
625 | 602 | API Changes | ||
626 | 603 | *********** | ||
627 | 604 | |||
628 | 605 | * ``bzrlib.textui`` (vestigial module) removed. (Martin Pool) | ||
629 | 606 | |||
630 | 607 | * The Launchpad plugin now has a function ``login`` which will log in to | ||
631 | 608 | Launchpad with launchpadlib, and ``load_branch`` which will return the | ||
632 | 609 | Launchpad Branch object corresponding to a given Bazaar Branch object. | ||
633 | 610 | (Jonathan Lange) | ||
634 | 611 | |||
635 | 612 | Internals | ||
636 | 613 | ********* | ||
637 | 614 | |||
638 | 615 | * New test Feature: ``ModuleAvailableFeature``. It is designed to make it | ||
639 | 616 | easier to handle what tests you want to run based on what modules can be | ||
640 | 617 | imported. (Rather than lots of custom-implemented features that were | ||
641 | 618 | basically copy-and-pasted.) (John Arbash Meinel) | ||
642 | 619 | |||
643 | 620 | * ``osutils.timer_func()`` can be used to get either ``time.time()`` or | ||
644 | 621 | ``time.clock()`` when you want to do performance timing. | ||
645 | 622 | ``time.time()`` is limited to 15ms resolution on Windows, but | ||
646 | 623 | ``time.clock()`` gives CPU and not wall-clock time on other platforms. | ||
647 | 624 | (John Arbash Meinel) | ||
648 | 625 | |||
649 | 626 | * Several code paths that were calling ``Transport.get().read()`` have | ||
650 | 627 | been changed to the equalivent ``Transport.get_bytes()``. The main | ||
651 | 628 | difference is that the latter will explicitly call ``file.close()``, | ||
652 | 629 | rather than expecting the garbage collector to handle it. This helps | ||
653 | 630 | with some race conditions on Windows during the test suite and sftp | ||
654 | 631 | tests. (John Arbash Meinel) | ||
655 | 632 | |||
656 | 633 | Testing | ||
657 | 634 | ******* | ||
658 | 635 | |||
659 | 636 | * TestCaseWithMemoryTransport no longer sets $HOME and $BZR_HOME to | ||
660 | 637 | unicode strings. (Michael Hudson, #464174) | ||
661 | 638 | |||
662 | 639 | |||
663 | 114 | bzr 2.0.3 | 640 | bzr 2.0.3 |
664 | 115 | ######### | 641 | ######### |
665 | 116 | 642 | ||
666 | @@ -140,6 +666,233 @@ | |||
667 | 140 | * Improve "Binary files differ" hunk handling. (Aaron Bentley, #436325) | 666 | * Improve "Binary files differ" hunk handling. (Aaron Bentley, #436325) |
668 | 141 | 667 | ||
669 | 142 | 668 | ||
670 | 669 | bzr 2.1.0b3 | ||
671 | 670 | ########### | ||
672 | 671 | |||
673 | 672 | :Codename: after sprint recovery | ||
674 | 673 | :2.1.0b3: 2009-11-16 | ||
675 | 674 | |||
676 | 675 | This release was pushed up from its normal release cycle due to a | ||
677 | 676 | regression in python 2.4 compatibility in 2.1.0b2. Since this regression | ||
678 | 677 | was caught before 2.1.0b2 was officially announced, the full changelog | ||
679 | 678 | includes both 2.1.0b3 and 2.1.0b2 changes. | ||
680 | 679 | |||
681 | 680 | Highlights of 2.1.0b3 are: new globbing code for all commands on Windows, | ||
682 | 681 | the test suite now conforms to python's trunk enhanced semantics (skip, | ||
683 | 682 | etc.), and ``bzr info -v`` will now report the correct branch and repo | ||
684 | 683 | formats for Remote objects. | ||
685 | 684 | |||
686 | 685 | |||
687 | 686 | New Features | ||
688 | 687 | ************ | ||
689 | 688 | |||
690 | 689 | * Users can define a shelve editor to provide shelf functionality at a | ||
691 | 690 | granularity finer than per-patch-hunk. (Aaron Bentley) | ||
692 | 691 | |||
693 | 692 | Bug Fixes | ||
694 | 693 | ********* | ||
695 | 694 | |||
696 | 695 | * Fix for shell completion and short options. (Benoît PIERRE) | ||
697 | 696 | |||
698 | 697 | * Hooks daughter classes should always call the base constructor. | ||
699 | 698 | (Alexander Belchenko, Vincent Ladeuil, #389648) | ||
700 | 699 | |||
701 | 700 | * Improve "Binary files differ" hunk handling. (Aaron Bentley, #436325) | ||
702 | 701 | |||
703 | 702 | * On Windows, do glob expansion at the command-line level (as is usually | ||
704 | 703 | done in bash, etc.) This means that *all* commands get glob expansion | ||
705 | 704 | (bzr status, bzr add, bzr mv, etc). It uses a custom command line | ||
706 | 705 | parser, which allows us to know if a given section was quoted. It means | ||
707 | 706 | you can now do ``bzr ignore "*.py"``. | ||
708 | 707 | (John Arbash Meinel, #425510, #426410, #194450) | ||
709 | 708 | |||
710 | 709 | * Sanitize commit messages that come in from the '-m' flag. We translate | ||
711 | 710 | '\r\n' => '\n' and a plain '\r' => '\n'. The storage layer doesn't | ||
712 | 711 | allow those because XML store silently translate it anyway. (The parser | ||
713 | 712 | auto-translates \r\n => \n in ways that are hard for us to catch.) | ||
714 | 713 | |||
715 | 714 | * Show correct branch and repository format descriptions in | ||
716 | 715 | ``bzr info -v`` on a smart server location. (Andrew Bennetts, #196080) | ||
717 | 716 | |||
718 | 717 | * The fix for bug #186920 accidentally broke compatibility with python | ||
719 | 718 | 2.4. (Vincent Ladeuil, #475585) | ||
720 | 719 | |||
721 | 720 | * Using ``Repository.get_commit_builder().record_iter_changes()`` now | ||
722 | 721 | correctly sets ``self.inv_sha1`` to a sha1 string and | ||
723 | 722 | ``self.new_inventory`` to an Inventory instance after calling | ||
724 | 723 | ``self.finish_inventory()``. (Previously it accidently set both values | ||
725 | 724 | as a tuple on ``self.inv_sha1``. This was missed because | ||
726 | 725 | ``repo.add_revision`` ignores the supplied inventory sha1 and recomputes | ||
727 | 726 | the sha1 from the repo directly. (John Arbash Meinel) | ||
728 | 727 | |||
729 | 728 | * Shelve command refuse to run if there is no real terminal. | ||
730 | 729 | (Alexander Belchenko) | ||
731 | 730 | |||
732 | 731 | * Avoid unnecessarily flushing of trace file; it's now unbuffered at the | ||
733 | 732 | Python level. (Martin Pool) | ||
734 | 733 | |||
735 | 734 | Documentation | ||
736 | 735 | ************* | ||
737 | 736 | |||
738 | 737 | * Include Japanese translations for documentation (Inada Naoki) | ||
739 | 738 | |||
740 | 739 | * New API ``ui_factory.make_output_stream`` to be used for sending bulk | ||
741 | 740 | (rather than user-interaction) data to stdout. This automatically | ||
742 | 741 | coordinates with progress bars or other terminal activity, and can be | ||
743 | 742 | overridden by GUIs. | ||
744 | 743 | (Martin Pool, 493944) | ||
745 | 744 | |||
746 | 745 | Internals | ||
747 | 746 | ********* | ||
748 | 747 | |||
749 | 748 | * Some of the core groupcompress functionality now releases the GIL before | ||
750 | 749 | operation. Similar to how zlib and bz2 operate without the GIL in the | ||
751 | 750 | core compression and decompression routines. (John Arbash Meinel) | ||
752 | 751 | |||
753 | 752 | Testing | ||
754 | 753 | ******* | ||
755 | 754 | |||
756 | 755 | * -Dhpssvfs will now trigger on ``RemoteBzrDir._ensure_real``, providing | ||
757 | 756 | more debugging of VFS access triggers. (Robert Collins) | ||
758 | 757 | |||
759 | 758 | * KnownFailure is now signalled to ``ExtendedTestResult`` using the same | ||
760 | 759 | method that Python 2.7 uses - ``addExpectedFailure``. (Robert Collins) | ||
761 | 760 | |||
762 | 761 | * ``--parallel=fork`` is now compatible with --subunit. | ||
763 | 762 | (Robert Collins, Vincent Ladeuil, #419776) | ||
764 | 763 | |||
765 | 764 | * Reporting of failures shows test ids not descriptions and thus shows | ||
766 | 765 | parameterised tests correctly. (Robert Collins) | ||
767 | 766 | |||
768 | 767 | * TestNotApplicable is now handled within the TestCase.run method rather | ||
769 | 768 | than being looked for within ``ExtendedTestResult.addError``. This | ||
770 | 769 | provides better handling with other ``TestResult`` objects, degrading to | ||
771 | 770 | sucess rather than error. (Robert Collins) | ||
772 | 771 | |||
773 | 772 | * The private method ``_testConcluded`` on ``ExtendedTestResult`` has been | ||
774 | 773 | removed - it was empty and unused. (Robert Collins) | ||
775 | 774 | |||
776 | 775 | * UnavailableFeature is now handled within the TestCase.run method rather | ||
777 | 776 | than being looked for within addError. If the Result object does not | ||
778 | 777 | have an addNotSupported method, addSkip is attempted instead, and | ||
779 | 778 | failing that addSuccess. (Robert Collins) | ||
780 | 779 | |||
781 | 780 | * When a TestResult does not have an addSkip method, skipped tests are now | ||
782 | 781 | reported as successful tests, rather than as errors. This change is | ||
783 | 782 | to make it possible to get a clean test run with a less capable | ||
784 | 783 | TestResult. (Robert Collins) | ||
785 | 784 | |||
786 | 785 | |||
787 | 786 | |||
788 | 787 | bzr 2.1.0b2 | ||
789 | 788 | ########### | ||
790 | 789 | |||
791 | 790 | :Codename: a load off my mind | ||
792 | 791 | :2.1.0b2: 2009-11-02 | ||
793 | 792 | |||
794 | 793 | This is our second feature-filled release since 2.0, pushing us down the | ||
795 | 794 | path to a 2.1.0. Once again, all bugfixes in 2.0.2 are present in 2.1.0b2. | ||
796 | 795 | |||
797 | 796 | Key highlights in this release are: improved handling of | ||
798 | 797 | failures-during-cleanup for commit, fixing a long-standing bug with | ||
799 | 798 | ``bzr+http`` and shared repositories, all ``lp:`` urls to be resolved | ||
800 | 799 | behind proxies, and a new StaticTuple datatype, allowing us to reduce | ||
801 | 800 | memory consumption (50%) and garbage collector overhead (40% faster) for | ||
802 | 801 | many operations. | ||
803 | 802 | |||
804 | 803 | * A new ``--concurrency`` option has been added as well as an associated | ||
805 | 804 | BZR_CONCURRENCY environment variable to specify the number of | ||
806 | 805 | processes that can be run concurrently when running ``bzr selftest``. The | ||
807 | 806 | command-line option overrides the environment variable if both are | ||
808 | 807 | specified. If none is specified. the number of processes is obtained | ||
809 | 808 | from the OS as before. (Matt Nordhoff, Vincent Ladeuil) | ||
810 | 809 | |||
811 | 810 | Bug Fixes | ||
812 | 811 | ********* | ||
813 | 812 | |||
814 | 813 | * ``bzr+http`` servers no longer give spurious jail break errors when | ||
815 | 814 | serving branches inside a shared repository. (Andrew Bennetts, #348308) | ||
816 | 815 | |||
817 | 816 | * Errors during commit are handled more robustly so that knock-on errors | ||
818 | 817 | are less likely to occur, and will not obscure the original error if | ||
819 | 818 | they do occur. This fixes some causes of ``TooManyConcurrentRequests`` | ||
820 | 819 | and similar errors. (Andrew Bennetts, #429747, #243391) | ||
821 | 820 | |||
822 | 821 | * Launchpad urls can now be resolved from behind proxies. | ||
823 | 822 | (Gordon Tyler, Vincent Ladeuil, #186920) | ||
824 | 823 | |||
825 | 824 | * Reduce the strictness for StaticTuple, instead add a debug flag | ||
826 | 825 | ``-Dstatic_tuple`` which will change apis to be strict and raise errors. | ||
827 | 826 | This way, most users won't see failures, but developers can improve | ||
828 | 827 | internals. (John Arbash Meinel, #471193) | ||
829 | 828 | |||
830 | 829 | * TreeTransform.adjust_path updates the limbo paths of descendants of adjusted | ||
831 | 830 | files. (Aaron Bentley) | ||
832 | 831 | |||
833 | 832 | * Unicode paths are now handled correctly and consistently by the smart | ||
834 | 833 | server. (Andrew Bennetts, Michael Hudson, #458762) | ||
835 | 834 | |||
836 | 835 | Improvements | ||
837 | 836 | ************ | ||
838 | 837 | |||
839 | 838 | * When reading index files, we now use a ``StaticTuple`` rather than a | ||
840 | 839 | plain ``tuple`` object. This generally gives a 20% decrease in peak | ||
841 | 840 | memory, and can give a performance boost up to 40% on large projects. | ||
842 | 841 | (John Arbash Meinel) | ||
843 | 842 | |||
844 | 843 | * Peak memory under certain operations has been reduced significantly. | ||
845 | 844 | (eg, 'bzr branch launchpad standalone' is cut in half) | ||
846 | 845 | (John Arbash Meinel) | ||
847 | 846 | |||
848 | 847 | Documentation | ||
849 | 848 | ************* | ||
850 | 849 | |||
851 | 850 | * Filtered views user documentation upgraded to refer to format 2a | ||
852 | 851 | instead of pre-2.0 formats. (Ian Clatworthy) | ||
853 | 852 | |||
854 | 853 | API Changes | ||
855 | 854 | *********** | ||
856 | 855 | |||
857 | 856 | * Remove deprecated ``CLIUIFactory``. (Martin Pool) | ||
858 | 857 | |||
859 | 858 | * ``UIFactory`` now has new ``show_error``, ``show_message`` and | ||
860 | 859 | ``show_warning`` methods, which can be hooked by non-text UIs. | ||
861 | 860 | (Martin Pool) | ||
862 | 861 | |||
863 | 862 | Internals | ||
864 | 863 | ********* | ||
865 | 864 | |||
866 | 865 | * Added ``bzrlib._simple_set_pyx``. This is a hybrid between a Set and a | ||
867 | 866 | Dict (it only holds keys, but you can lookup the object located at a | ||
868 | 867 | given key). It has significantly reduced memory consumption versus the | ||
869 | 868 | builtin objects (1/2 the size of Set, 1/3rd the size of Dict). This is | ||
870 | 869 | used as the interning structure for StaticTuple objects. | ||
871 | 870 | (John Arbash Meinel) | ||
872 | 871 | |||
873 | 872 | * ``bzrlib._static_tuple_c.StaticTuple`` is now available and used by | ||
874 | 873 | the btree index parser and the chk map parser. This class functions | ||
875 | 874 | similarly to ``tuple`` objects. However, it can only point to a limited | ||
876 | 875 | collection of types. (Currently StaticTuple, str, unicode, None, bool, | ||
877 | 876 | int, long, float, but not subclasses). This allows us to remove it from | ||
878 | 877 | the garbage collector (it cannot be in a cycle), it also allows us to | ||
879 | 878 | intern the objects. In testing, this can reduce peak memory by 20-40%, | ||
880 | 879 | and significantly improve performance by removing objects from being | ||
881 | 880 | inspected by the garbage collector. (John Arbash Meinel) | ||
882 | 881 | |||
883 | 882 | * ``GroupCompressBlock._ensure_content()`` will now release the | ||
884 | 883 | ``zlib.decompressobj()`` when the first request is for all of the | ||
885 | 884 | content. (Previously it would only be released if you made a request for | ||
886 | 885 | part of the content, and then all of it later.) This turns out to be a | ||
887 | 886 | significant memory savings, as a ``zstream`` carries around approx 260kB | ||
888 | 887 | of internal state and buffers. (For branching bzr.dev this drops peak | ||
889 | 888 | memory from 382MB => 345MB.) (John Arbash Meinel) | ||
890 | 889 | |||
891 | 890 | * When streaming content between ``2a`` format repositories, we now clear | ||
892 | 891 | caches from earlier versioned files. (So 'revisions' is cleared when we | ||
893 | 892 | start reading 'inventories', etc.) This can have a significant impact on | ||
894 | 893 | peak memory for initial copies (~200MB). (John Arbash Meinel) | ||
895 | 894 | |||
896 | 895 | |||
897 | 143 | bzr 2.0.2 | 896 | bzr 2.0.2 |
898 | 144 | ######### | 897 | ######### |
899 | 145 | 898 | ||
900 | @@ -187,6 +940,219 @@ | |||
901 | 187 | instead of pre-2.0 formats. (Ian Clatworthy) | 940 | instead of pre-2.0 formats. (Ian Clatworthy) |
902 | 188 | 941 | ||
903 | 189 | 942 | ||
904 | 943 | bzr 2.1.0b1 | ||
905 | 944 | ########### | ||
906 | 945 | |||
907 | 946 | :Codename: While the cat is away | ||
908 | 947 | :2.1.0b1: 2009-10-14 | ||
909 | 948 | |||
910 | 949 | This is the first development release in the new split "stable" and | ||
911 | 950 | "development" series. As such, the release is a snapshot of bzr.dev | ||
912 | 951 | without creating a release candidate first. This release includes a | ||
913 | 952 | fair amount of internal changes, with deprecated code being removed, | ||
914 | 953 | and several new feature developments. People looking for a stable code | ||
915 | 954 | base with only bugfixes should focus on the 2.0.1 release. All bugfixes | ||
916 | 955 | present in 2.0.1 are present in 2.1.0b1. | ||
917 | 956 | |||
918 | 957 | Highlights include support for ``bzr+ssh://host/~/homedir`` style urls, | ||
919 | 958 | finer control over the plugin search path via extended BZR_PLUGIN_PATH | ||
920 | 959 | syntax, visible warnings when extension modules fail to load, and improved | ||
921 | 960 | error handling during unlocking. | ||
922 | 961 | |||
923 | 962 | |||
924 | 963 | New Features | ||
925 | 964 | ************ | ||
926 | 965 | |||
927 | 966 | * Bazaar can now send mail through Apple OS X Mail.app. | ||
928 | 967 | (Brian de Alwis) | ||
929 | 968 | |||
930 | 969 | * ``bzr+ssh`` and ``bzr`` paths can now be relative to home directories | ||
931 | 970 | specified in the URL. Paths starting with a path segment of ``~`` are | ||
932 | 971 | relative to the home directory of the user running the server, and paths | ||
933 | 972 | starting with ``~user`` are relative to the home directory of the named | ||
934 | 973 | user. For example, for a user "bob" with a home directory of | ||
935 | 974 | ``/home/bob``, these URLs are all equivalent: | ||
936 | 975 | |||
937 | 976 | * ``bzr+ssh://bob@host/~/repo`` | ||
938 | 977 | * ``bzr+ssh://bob@host/~bob/repo`` | ||
939 | 978 | * ``bzr+ssh://bob@host/home/bob/repo`` | ||
940 | 979 | |||
941 | 980 | If ``bzr serve`` was invoked with a ``--directory`` argument, then no | ||
942 | 981 | home directories outside that directory will be accessible via this | ||
943 | 982 | method. | ||
944 | 983 | |||
945 | 984 | This is a feature of ``bzr serve``, so pre-2.1 clients will | ||
946 | 985 | automatically benefit from this feature when ``bzr`` on the server is | ||
947 | 986 | upgraded. (Andrew Bennetts, #109143) | ||
948 | 987 | |||
949 | 988 | * Extensions can now be compiled if either Cython or Pyrex is available. | ||
950 | 989 | Currently Pyrex is preferred, but that may change in the future. | ||
951 | 990 | (Arkanes) | ||
952 | 991 | |||
953 | 992 | * Give more control on BZR_PLUGIN_PATH by providing a way to refer to or | ||
954 | 993 | disable the user, site and core plugin directories. | ||
955 | 994 | (Vincent Ladeuil, #412930, #316192, #145612) | ||
956 | 995 | |||
957 | 996 | Bug Fixes | ||
958 | 997 | ********* | ||
959 | 998 | |||
960 | 999 | * Bazaar's native protocol code now correctly handles EINTR, which most | ||
961 | 1000 | noticeably occurs if you break in to the debugger while connected to a | ||
962 | 1001 | bzr+ssh server. You can now can continue from the debugger (by typing | ||
963 | 1002 | 'c') and the process continues. However, note that pressing C-\ in the | ||
964 | 1003 | shell may still kill the SSH process, which is bug 162509, so you must | ||
965 | 1004 | sent a signal to the bzr process specifically, for example by typing | ||
966 | 1005 | ``kill -QUIT PID`` in another shell. (Martin Pool, #341535) | ||
967 | 1006 | |||
968 | 1007 | * ``bzr add`` in a tree that has files with ``\r`` or ``\n`` in the | ||
969 | 1008 | filename will issue a warning and skip over those files. | ||
970 | 1009 | (Robert Collins, #3918) | ||
971 | 1010 | |||
972 | 1011 | * ``bzr dpush`` now aborts if uncommitted changes (including pending merges) | ||
973 | 1012 | are present in the working tree. The configuration option ``dpush_strict`` | ||
974 | 1013 | can be used to set the default for this behavior. | ||
975 | 1014 | (Vincent Ladeuil, #438158) | ||
976 | 1015 | |||
977 | 1016 | * ``bzr merge`` and ``bzr remove-tree`` now requires --force if pending | ||
978 | 1017 | merges are present in the working tree. | ||
979 | 1018 | (Vincent Ladeuil, #426344) | ||
980 | 1019 | |||
981 | 1020 | * Clearer message when Bazaar runs out of memory, instead of a ``MemoryError`` | ||
982 | 1021 | traceback. (Martin Pool, #109115) | ||
983 | 1022 | |||
984 | 1023 | * Don't give a warning on Windows when failing to import ``_readdir_pyx`` | ||
985 | 1024 | as it is never built. (John Arbash Meinel, #430645) | ||
986 | 1025 | |||
987 | 1026 | * Don't restrict the command name used to run the test suite. | ||
988 | 1027 | (Vincent Ladeuil, #419950) | ||
989 | 1028 | |||
990 | 1029 | * ftp transports were built differently when the kerberos python module was | ||
991 | 1030 | present leading to obscure failures related to ASCII/BINARY modes. | ||
992 | 1031 | (Vincent Ladeuil, #443041) | ||
993 | 1032 | |||
994 | 1033 | * Network streams now decode adjacent records of the same type into a | ||
995 | 1034 | single stream, reducing layering churn. (Robert Collins) | ||
996 | 1035 | |||
997 | 1036 | * PreviewTree behaves correctly when get_file_mtime is invoked on an unmodified | ||
998 | 1037 | file. (Aaron Bentley, #251532) | ||
999 | 1038 | |||
1000 | 1039 | * Registry objects should not use iteritems() when asked to use items(). | ||
1001 | 1040 | (Vincent Ladeuil, #430510) | ||
1002 | 1041 | |||
1003 | 1042 | * Weave based repositories couldn't be cloned when committers were using | ||
1004 | 1043 | domains or user ids embedding '.sig'. Now they can. | ||
1005 | 1044 | (Matthew Fuller, Vincent Ladeuil, #430868) | ||
1006 | 1045 | |||
1007 | 1046 | Improvements | ||
1008 | 1047 | ************ | ||
1009 | 1048 | |||
1010 | 1049 | * Revision specifiers can now be given in a more DWIM form, without | ||
1011 | 1050 | needing explicit prefixes for specifiers like tags or revision id's. | ||
1012 | 1051 | See ``bzr help revisionspec`` for full details. (Matthew Fuller) | ||
1013 | 1052 | |||
1014 | 1053 | * Bazaar gives a warning before exiting, and writes into ``.bzr.log``, if | ||
1015 | 1054 | compiled extensions can't be loaded. This typically indicates a | ||
1016 | 1055 | packaging or installation problem. In this case Bazaar will keep | ||
1017 | 1056 | running using pure-Python versions, but this may be substantially | ||
1018 | 1057 | slower. The warning can be disabled by setting | ||
1019 | 1058 | ``ignore_missing_extensions = True`` in ``bazaar.conf``. | ||
1020 | 1059 | See also <https://answers.launchpad.net/bzr/+faq/703>. | ||
1021 | 1060 | (Martin Pool, #406113, #430529) | ||
1022 | 1061 | |||
1023 | 1062 | * Secondary errors that occur during Branch.unlock and Repository.unlock | ||
1024 | 1063 | no longer obscure the original error. These methods now use a new | ||
1025 | 1064 | decorator, ``only_raises``. This fixes many causes of | ||
1026 | 1065 | ``TooManyConcurrentRequests`` and similar errors. | ||
1027 | 1066 | (Andrew Bennetts, #429747) | ||
1028 | 1067 | |||
1029 | 1068 | Documentation | ||
1030 | 1069 | ************* | ||
1031 | 1070 | |||
1032 | 1071 | * Describe the new shell-like test feature. (Vincent Ladeuil) | ||
1033 | 1072 | |||
1034 | 1073 | * Help on hooks no longer says 'Not deprecated' for hooks that are | ||
1035 | 1074 | currently supported. (Ian Clatworthy, #422415) | ||
1036 | 1075 | |||
1037 | 1076 | API Changes | ||
1038 | 1077 | *********** | ||
1039 | 1078 | |||
1040 | 1079 | * ``bzrlib.user_encoding`` has been removed; use | ||
1041 | 1080 | ``bzrlib.osutils.get_user_encoding`` instead. (Martin Pool) | ||
1042 | 1081 | |||
1043 | 1082 | * ``bzrlib.tests`` now uses ``stopTestRun`` for its ``TestResult`` | ||
1044 | 1083 | subclasses - the same as python's unittest module. (Robert Collins) | ||
1045 | 1084 | |||
1046 | 1085 | * ``diff._get_trees_to_diff`` has been renamed to | ||
1047 | 1086 | ``diff.get_trees_and_branches_to_diff``. It is now a public API, and it | ||
1048 | 1087 | returns the old and new branches. (Gary van der Merwe) | ||
1049 | 1088 | |||
1050 | 1089 | * ``bzrlib.trace.log_error``, ``error`` and ``info`` have been deprecated. | ||
1051 | 1090 | (Martin Pool) | ||
1052 | 1091 | |||
1053 | 1092 | * ``MutableTree.has_changes()`` does not require a tree parameter anymore. It | ||
1054 | 1093 | now defaults to comparing to the basis tree. It now checks for pending | ||
1055 | 1094 | merges too. ``Merger.check_basis`` has been deprecated and replaced by the | ||
1056 | 1095 | corresponding has_changes() calls. ``Merge.compare_basis``, | ||
1057 | 1096 | ``Merger.file_revisions`` and ``Merger.ensure_revision_trees`` have also | ||
1058 | 1097 | been deprecated. | ||
1059 | 1098 | (Vincent Ladeuil, #440631) | ||
1060 | 1099 | |||
1061 | 1100 | * ``ProgressTask.note`` is deprecated. | ||
1062 | 1101 | (Martin Pool) | ||
1063 | 1102 | |||
1064 | 1103 | Internals | ||
1065 | 1104 | ********* | ||
1066 | 1105 | |||
1067 | 1106 | * Added ``-Drelock`` debug flag. It will ``note`` a message every time a | ||
1068 | 1107 | repository or branch object is unlocked then relocked the same way. | ||
1069 | 1108 | (Andrew Bennetts) | ||
1070 | 1109 | |||
1071 | 1110 | * ``BTreeLeafParser.extract_key`` has been tweaked slightly to reduce | ||
1072 | 1111 | mallocs while parsing the index (approx 3=>1 mallocs per key read). | ||
1073 | 1112 | This results in a 10% speedup while reading an index. | ||
1074 | 1113 | (John Arbash Meinel) | ||
1075 | 1114 | |||
1076 | 1115 | * The ``bzrlib.lsprof`` module has a new class ``BzrProfiler`` which makes | ||
1077 | 1116 | profiling in some situations like callbacks and generators easier. | ||
1078 | 1117 | (Robert Collins) | ||
1079 | 1118 | |||
1080 | 1119 | Testing | ||
1081 | 1120 | ******* | ||
1082 | 1121 | |||
1083 | 1122 | * Passing ``--lsprof-tests -v`` to bzr selftest will cause lsprof output to | ||
1084 | 1123 | be output for every test. Note that this is very verbose! (Robert Collins) | ||
1085 | 1124 | |||
1086 | 1125 | * Setting ``BZR_TEST_PDB=1`` when running selftest will cause a pdb | ||
1087 | 1126 | post_mortem to be triggered when a test failure occurs. (Robert Collins) | ||
1088 | 1127 | |||
1089 | 1128 | * Shell-like tests can now be written. Code in ``bzrlib/tests/script.py`` , | ||
1090 | 1129 | documentation in ``developers/testing.txt`` for details. | ||
1091 | 1130 | (Vincent Ladeuil) | ||
1092 | 1131 | |||
1093 | 1132 | * Some tests could end up with the same id, that was dormant for | ||
1094 | 1133 | a long time. | ||
1095 | 1134 | (Vincent Ladeuil, #442980) | ||
1096 | 1135 | |||
1097 | 1136 | * Stop showing the number of tests due to missing features in the test | ||
1098 | 1137 | progress bar. (Martin Pool) | ||
1099 | 1138 | |||
1100 | 1139 | * Test parameterisation now does a shallow copy, not a deep copy of the test | ||
1101 | 1140 | to be parameterised. This is not expected to break external use of test | ||
1102 | 1141 | parameterisation, and is substantially faster. (Robert Collins) | ||
1103 | 1142 | |||
1104 | 1143 | * Tests that try to open a bzr dir on an arbitrary transport will now | ||
1105 | 1144 | fail unless they have explicitly permitted the transport via | ||
1106 | 1145 | ``self.permit_url``. The standard test factories such as ``self.get_url`` | ||
1107 | 1146 | will permit the urls they provide automatically, so only exceptional | ||
1108 | 1147 | tests should need to do this. (Robert Collins) | ||
1109 | 1148 | |||
1110 | 1149 | * The break-in test no longer cares about clean shutdown of the child, | ||
1111 | 1150 | instead it is happy if the debugger starts up. (Robert Collins) | ||
1112 | 1151 | |||
1113 | 1152 | * The full test suite is expected to pass when the C extensions are not | ||
1114 | 1153 | present. (Vincent Ladeuil, #430749) | ||
1115 | 1154 | |||
1116 | 1155 | |||
1117 | 190 | bzr 2.0.1 | 1156 | bzr 2.0.1 |
1118 | 191 | ######### | 1157 | ######### |
1119 | 192 | 1158 | ||
1120 | @@ -290,6 +1256,7 @@ | |||
1121 | 290 | to "land in 2.0.0". (Changes how bzrlib._format_version_tuple() handles | 1256 | to "land in 2.0.0". (Changes how bzrlib._format_version_tuple() handles |
1122 | 291 | micro = 0.) (John Arbash Meinel) | 1257 | micro = 0.) (John Arbash Meinel) |
1123 | 292 | 1258 | ||
1124 | 1259 | |||
1125 | 293 | bzr 2.0.0rc2 | 1260 | bzr 2.0.0rc2 |
1126 | 294 | ############ | 1261 | ############ |
1127 | 295 | 1262 | ||
1128 | @@ -346,10 +1313,6 @@ | |||
1129 | 346 | ghosts in its mainline. (Evaluating None as a tuple is bad.) | 1313 | ghosts in its mainline. (Evaluating None as a tuple is bad.) |
1130 | 347 | (John Arbash Meinel, #419241) | 1314 | (John Arbash Meinel, #419241) |
1131 | 348 | 1315 | ||
1132 | 349 | * Fix a segmentation fault when computing the ``merge_sort`` of a graph | ||
1133 | 350 | that has a ghost in the mainline ancestry. | ||
1134 | 351 | (John Arbash Meinel, #419241) | ||
1135 | 352 | |||
1136 | 353 | * ``groupcompress`` sort order is now more stable, rather than relying on | 1316 | * ``groupcompress`` sort order is now more stable, rather than relying on |
1137 | 354 | ``topo_sort`` ordering. The implementation is now | 1317 | ``topo_sort`` ordering. The implementation is now |
1138 | 355 | ``KnownGraph.gc_sort``. (John Arbash Meinel) | 1318 | ``KnownGraph.gc_sort``. (John Arbash Meinel) |
1139 | @@ -417,6 +1380,9 @@ | |||
1140 | 417 | Bug Fixes | 1380 | Bug Fixes |
1141 | 418 | ********* | 1381 | ********* |
1142 | 419 | 1382 | ||
1143 | 1383 | * Further tweaks to handling of ``bzr add`` messages about ignored files. | ||
1144 | 1384 | (Jason Spashett, #76616) | ||
1145 | 1385 | |||
1146 | 420 | * Fetches were being requested in 'groupcompress' order, but weren't | 1386 | * Fetches were being requested in 'groupcompress' order, but weren't |
1147 | 421 | recombining the groups. Thus they would 'fragment' to get the correct | 1387 | recombining the groups. Thus they would 'fragment' to get the correct |
1148 | 422 | order, but not 'recombine' to actually benefit from it. Until we get | 1388 | order, but not 'recombine' to actually benefit from it. Until we get |
1149 | @@ -462,7 +1428,7 @@ | |||
1150 | 462 | * ``bzr shelve`` and ``bzr unshelve`` now work on windows. | 1428 | * ``bzr shelve`` and ``bzr unshelve`` now work on windows. |
1151 | 463 | (Robert Collins, #305006) | 1429 | (Robert Collins, #305006) |
1152 | 464 | 1430 | ||
1154 | 465 | * Commit of specific files no longer prevents using the the iter_changes | 1431 | * Commit of specific files no longer prevents using the iter_changes |
1155 | 466 | codepath. On 2a repositories, commit of specific files should now be as | 1432 | codepath. On 2a repositories, commit of specific files should now be as |
1156 | 467 | fast, or slightly faster, than a full commit. (Robert Collins) | 1433 | fast, or slightly faster, than a full commit. (Robert Collins) |
1157 | 468 | 1434 | ||
1158 | @@ -486,9 +1452,6 @@ | |||
1159 | 486 | classes changed to manage lock lifetime of the trees they open in a way | 1452 | classes changed to manage lock lifetime of the trees they open in a way |
1160 | 487 | consistent with reader-exclusive locks. (Robert Collins, #305006) | 1453 | consistent with reader-exclusive locks. (Robert Collins, #305006) |
1161 | 488 | 1454 | ||
1162 | 489 | Internals | ||
1163 | 490 | ********* | ||
1164 | 491 | |||
1165 | 492 | Testing | 1455 | Testing |
1166 | 493 | ******* | 1456 | ******* |
1167 | 494 | 1457 | ||
1168 | @@ -535,6 +1498,9 @@ | |||
1169 | 535 | a tree with content filtering where the size of the canonical form | 1498 | a tree with content filtering where the size of the canonical form |
1170 | 536 | cannot be cheaply determined. (Martin Pool) | 1499 | cannot be cheaply determined. (Martin Pool) |
1171 | 537 | 1500 | ||
1172 | 1501 | * When manually creating transport servers in test cases, a new helper | ||
1173 | 1502 | ``TestCase.start_server`` that registers a cleanup and starts the server | ||
1174 | 1503 | should be used. (Robert Collins) | ||
1175 | 538 | 1504 | ||
1176 | 539 | bzr 1.18 | 1505 | bzr 1.18 |
1177 | 540 | ######## | 1506 | ######## |
1178 | @@ -872,6 +1838,17 @@ | |||
1179 | 872 | ``countTestsCases``. (Robert Collins) | 1838 | ``countTestsCases``. (Robert Collins) |
1180 | 873 | 1839 | ||
1181 | 874 | 1840 | ||
1182 | 1841 | bzr 1.17.1 (unreleased) | ||
1183 | 1842 | ####################### | ||
1184 | 1843 | |||
1185 | 1844 | Bug Fixes | ||
1186 | 1845 | ********* | ||
1187 | 1846 | |||
1188 | 1847 | * The optional ``_knit_load_data_pyx`` C extension was never being | ||
1189 | 1848 | imported. This caused significant slowdowns when reading data from | ||
1190 | 1849 | knit format repositories. (Andrew Bennetts, #405653) | ||
1191 | 1850 | |||
1192 | 1851 | |||
1193 | 875 | bzr 1.17 | 1852 | bzr 1.17 |
1194 | 876 | ######## | 1853 | ######## |
1195 | 877 | :Codename: so-late-its-brunch | 1854 | :Codename: so-late-its-brunch |
1196 | @@ -1220,7 +2197,7 @@ | |||
1197 | 1220 | ************ | 2197 | ************ |
1198 | 1221 | 2198 | ||
1199 | 1222 | * A new repository format ``2a`` has been added. This is a beta release | 2199 | * A new repository format ``2a`` has been added. This is a beta release |
1201 | 1223 | of the the brisbane-core (aka group-compress) project. This format now | 2200 | of the brisbane-core (aka group-compress) project. This format now |
1202 | 1224 | suitable for wider testing by advanced users willing to deal with some | 2201 | suitable for wider testing by advanced users willing to deal with some |
1203 | 1225 | bugs. We would appreciate test reports, either positive or negative. | 2202 | bugs. We would appreciate test reports, either positive or negative. |
1204 | 1226 | Format 2a is substantially smaller and faster for many operations on | 2203 | Format 2a is substantially smaller and faster for many operations on |
1205 | @@ -1372,6 +2349,9 @@ | |||
1206 | 1372 | Testing | 2349 | Testing |
1207 | 1373 | ******* | 2350 | ******* |
1208 | 1374 | 2351 | ||
1209 | 2352 | * ``make check`` no longer repeats the test run in ``LANG=C``. | ||
1210 | 2353 | (Martin Pool, #386180) | ||
1211 | 2354 | |||
1212 | 1375 | * The number of cores is now correctly detected on OSX. (John Szakmeister) | 2355 | * The number of cores is now correctly detected on OSX. (John Szakmeister) |
1213 | 1376 | 2356 | ||
1214 | 1377 | * The number of cores is also detected on Solaris and win32. (Vincent Ladeuil) | 2357 | * The number of cores is also detected on Solaris and win32. (Vincent Ladeuil) |
1215 | @@ -1946,7 +2926,7 @@ | |||
1216 | 1946 | 2926 | ||
1217 | 1947 | * Added ``bzrlib.inventory_delta`` module. This will be used for | 2927 | * Added ``bzrlib.inventory_delta`` module. This will be used for |
1218 | 1948 | serializing and deserializing inventory deltas for more efficient | 2928 | serializing and deserializing inventory deltas for more efficient |
1220 | 1949 | streaming on the the network. (Robert Collins, Andrew Bennetts) | 2929 | streaming on the network. (Robert Collins, Andrew Bennetts) |
1221 | 1950 | 2930 | ||
1222 | 1951 | * ``Branch._get_config`` has been added, which splits out access to the | 2931 | * ``Branch._get_config`` has been added, which splits out access to the |
1223 | 1952 | specific config file from the branch. This is used to let RemoteBranch | 2932 | specific config file from the branch. This is used to let RemoteBranch |
1224 | @@ -2141,7 +3121,7 @@ | |||
1225 | 2141 | * Multiple authors for a commit can now be recorded by using the "--author" | 3121 | * Multiple authors for a commit can now be recorded by using the "--author" |
1226 | 2142 | option multiple times. (James Westby, #185772) | 3122 | option multiple times. (James Westby, #185772) |
1227 | 2143 | 3123 | ||
1229 | 2144 | * New clean-tree command, from bzrtools. (Aaron Bentley, Jelmer Vernoij) | 3124 | * New clean-tree command, from bzrtools. (Aaron Bentley, Jelmer Vernooij) |
1230 | 2145 | 3125 | ||
1231 | 2146 | * New command ``bzr launchpad-open`` opens a Launchpad web page for that | 3126 | * New command ``bzr launchpad-open`` opens a Launchpad web page for that |
1232 | 2147 | branch in your web browser, as long as the branch is on Launchpad at all. | 3127 | branch in your web browser, as long as the branch is on Launchpad at all. |
1233 | @@ -4670,7 +5650,7 @@ | |||
1234 | 4670 | exception. (Andrew Bennetts) | 5650 | exception. (Andrew Bennetts) |
1235 | 4671 | 5651 | ||
1236 | 4672 | * New ``--debugflag``/``-E`` option to ``bzr selftest`` for setting | 5652 | * New ``--debugflag``/``-E`` option to ``bzr selftest`` for setting |
1238 | 4673 | options for debugging tests, these are complementary to the the -D | 5653 | options for debugging tests, these are complementary to the -D |
1239 | 4674 | options. The ``-Dselftest_debug`` global option has been replaced by the | 5654 | options. The ``-Dselftest_debug`` global option has been replaced by the |
1240 | 4675 | ``-E=allow_debug`` option for selftest. (Andrew Bennetts) | 5655 | ``-E=allow_debug`` option for selftest. (Andrew Bennetts) |
1241 | 4676 | 5656 | ||
1242 | @@ -5425,7 +6405,7 @@ | |||
1243 | 5425 | checkouts. (Aaron Bentley, #182040) | 6405 | checkouts. (Aaron Bentley, #182040) |
1244 | 5426 | 6406 | ||
1245 | 5427 | * Stop polluting /tmp when running selftest. | 6407 | * Stop polluting /tmp when running selftest. |
1247 | 5428 | (Vincent Ladeuil, #123623) | 6408 | (Vincent Ladeuil, #123363) |
1248 | 5429 | 6409 | ||
1249 | 5430 | * Switch from NFKC => NFC for normalization checks. NFC allows a few | 6410 | * Switch from NFKC => NFC for normalization checks. NFC allows a few |
1250 | 5431 | more characters which should be considered valid. | 6411 | more characters which should be considered valid. |
1251 | 5432 | 6412 | ||
1252 | === modified file 'bzr' | |||
1253 | --- bzr 2009-12-15 16:56:18 +0000 | |||
1254 | +++ bzr 2010-02-18 00:00:51 +0000 | |||
1255 | @@ -23,7 +23,7 @@ | |||
1256 | 23 | import warnings | 23 | import warnings |
1257 | 24 | 24 | ||
1258 | 25 | # update this on each release | 25 | # update this on each release |
1260 | 26 | _script_version = (2, 0, 4) | 26 | _script_version = (2, 1, 1) |
1261 | 27 | 27 | ||
1262 | 28 | if __doc__ is None: | 28 | if __doc__ is None: |
1263 | 29 | print "bzr does not support python -OO." | 29 | print "bzr does not support python -OO." |
1264 | 30 | 30 | ||
1265 | === modified file 'bzrlib/__init__.py' | |||
1266 | --- bzrlib/__init__.py 2010-01-21 19:35:57 +0000 | |||
1267 | +++ bzrlib/__init__.py 2010-02-18 00:00:52 +0000 | |||
1268 | @@ -31,16 +31,10 @@ | |||
1269 | 31 | import bzrlib.lazy_regex | 31 | import bzrlib.lazy_regex |
1270 | 32 | bzrlib.lazy_regex.install_lazy_compile() | 32 | bzrlib.lazy_regex.install_lazy_compile() |
1271 | 33 | 33 | ||
1272 | 34 | from bzrlib.osutils import get_user_encoding | ||
1273 | 35 | |||
1274 | 36 | 34 | ||
1275 | 37 | IGNORE_FILENAME = ".bzrignore" | 35 | IGNORE_FILENAME = ".bzrignore" |
1276 | 38 | 36 | ||
1277 | 39 | 37 | ||
1278 | 40 | # XXX: Deprecated as of bzr-1.17 use osutils.get_user_encoding() directly | ||
1279 | 41 | user_encoding = get_user_encoding() | ||
1280 | 42 | |||
1281 | 43 | |||
1282 | 44 | __copyright__ = "Copyright 2005, 2006, 2007, 2008, 2009 Canonical Ltd." | 38 | __copyright__ = "Copyright 2005, 2006, 2007, 2008, 2009 Canonical Ltd." |
1283 | 45 | 39 | ||
1284 | 46 | # same format as sys.version_info: "A tuple containing the five components of | 40 | # same format as sys.version_info: "A tuple containing the five components of |
1285 | @@ -50,10 +44,11 @@ | |||
1286 | 50 | # Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a | 44 | # Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a |
1287 | 51 | # releaselevel of 'dev' for unreleased under-development code. | 45 | # releaselevel of 'dev' for unreleased under-development code. |
1288 | 52 | 46 | ||
1290 | 53 | version_info = (2, 0, 4, 'final', 0) | 47 | version_info = (2, 1, 1, 'dev', 0) |
1291 | 54 | 48 | ||
1292 | 55 | # API compatibility version: bzrlib is currently API compatible with 1.15. | 49 | # API compatibility version: bzrlib is currently API compatible with 1.15. |
1294 | 56 | api_minimum_version = (1, 17, 0) | 50 | api_minimum_version = (2, 1, 0) |
1295 | 51 | |||
1296 | 57 | 52 | ||
1297 | 58 | def _format_version_tuple(version_info): | 53 | def _format_version_tuple(version_info): |
1298 | 59 | """Turn a version number 2, 3 or 5-tuple into a short string. | 54 | """Turn a version number 2, 3 or 5-tuple into a short string. |
1299 | 60 | 55 | ||
1300 | === modified file 'bzrlib/_annotator_pyx.pyx' | |||
1301 | --- bzrlib/_annotator_pyx.pyx 2010-01-05 04:59:57 +0000 | |||
1302 | +++ bzrlib/_annotator_pyx.pyx 2010-02-18 00:00:52 +0000 | |||
1303 | @@ -1,4 +1,4 @@ | |||
1305 | 1 | # Copyright (C) 2009 Canonical Ltd | 1 | # Copyright (C) 2009, 2010 Canonical Ltd |
1306 | 2 | # | 2 | # |
1307 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
1308 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
1309 | 5 | 5 | ||
1310 | === modified file 'bzrlib/_bencode_pyx.pyx' | |||
1311 | --- bzrlib/_bencode_pyx.pyx 2009-12-18 21:58:32 +0000 | |||
1312 | +++ bzrlib/_bencode_pyx.pyx 2010-02-18 00:00:52 +0000 | |||
1313 | @@ -1,4 +1,4 @@ | |||
1315 | 1 | # Copyright (C) 2007,2009 Canonical Ltd | 1 | # Copyright (C) 2007, 2009, 2010 Canonical Ltd |
1316 | 2 | # | 2 | # |
1317 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
1318 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
1319 | @@ -58,6 +58,13 @@ | |||
1320 | 58 | void D_UPDATE_TAIL(Decoder, int n) | 58 | void D_UPDATE_TAIL(Decoder, int n) |
1321 | 59 | void E_UPDATE_TAIL(Encoder, int n) | 59 | void E_UPDATE_TAIL(Encoder, int n) |
1322 | 60 | 60 | ||
1323 | 61 | # To maintain compatibility with older versions of pyrex, we have to use the | ||
1324 | 62 | # relative import here, rather than 'bzrlib._static_tuple_c' | ||
1325 | 63 | from _static_tuple_c cimport StaticTuple, StaticTuple_CheckExact, \ | ||
1326 | 64 | import_static_tuple_c | ||
1327 | 65 | |||
1328 | 66 | import_static_tuple_c() | ||
1329 | 67 | |||
1330 | 61 | 68 | ||
1331 | 62 | cdef class Decoder: | 69 | cdef class Decoder: |
1332 | 63 | """Bencode decoder""" | 70 | """Bencode decoder""" |
1333 | @@ -371,7 +378,8 @@ | |||
1334 | 371 | self._encode_int(x) | 378 | self._encode_int(x) |
1335 | 372 | elif PyLong_CheckExact(x): | 379 | elif PyLong_CheckExact(x): |
1336 | 373 | self._encode_long(x) | 380 | self._encode_long(x) |
1338 | 374 | elif PyList_CheckExact(x) or PyTuple_CheckExact(x): | 381 | elif (PyList_CheckExact(x) or PyTuple_CheckExact(x) |
1339 | 382 | or StaticTuple_CheckExact(x)): | ||
1340 | 375 | self._encode_list(x) | 383 | self._encode_list(x) |
1341 | 376 | elif PyDict_CheckExact(x): | 384 | elif PyDict_CheckExact(x): |
1342 | 377 | self._encode_dict(x) | 385 | self._encode_dict(x) |
1343 | 378 | 386 | ||
1344 | === modified file 'bzrlib/_btree_serializer_py.py' | |||
1345 | --- bzrlib/_btree_serializer_py.py 2009-03-23 14:59:43 +0000 | |||
1346 | +++ bzrlib/_btree_serializer_py.py 2010-02-18 00:00:50 +0000 | |||
1347 | @@ -1,4 +1,4 @@ | |||
1349 | 1 | # Copyright (C) 2008 Canonical Ltd | 1 | # Copyright (C) 2008, 2009, 2010 Canonical Ltd |
1350 | 2 | # | 2 | # |
1351 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
1352 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
1353 | @@ -17,27 +17,33 @@ | |||
1354 | 17 | 17 | ||
1355 | 18 | """B+Tree index parsing.""" | 18 | """B+Tree index parsing.""" |
1356 | 19 | 19 | ||
1357 | 20 | from bzrlib import static_tuple | ||
1358 | 21 | |||
1359 | 22 | |||
1360 | 20 | def _parse_leaf_lines(bytes, key_length, ref_list_length): | 23 | def _parse_leaf_lines(bytes, key_length, ref_list_length): |
1361 | 21 | lines = bytes.split('\n') | 24 | lines = bytes.split('\n') |
1362 | 22 | nodes = [] | 25 | nodes = [] |
1363 | 26 | as_st = static_tuple.StaticTuple.from_sequence | ||
1364 | 27 | stuple = static_tuple.StaticTuple | ||
1365 | 23 | for line in lines[1:]: | 28 | for line in lines[1:]: |
1366 | 24 | if line == '': | 29 | if line == '': |
1367 | 25 | return nodes | 30 | return nodes |
1368 | 26 | elements = line.split('\0', key_length) | 31 | elements = line.split('\0', key_length) |
1369 | 27 | # keys are tuples | 32 | # keys are tuples |
1371 | 28 | key = tuple(elements[:key_length]) | 33 | key = as_st(elements[:key_length]).intern() |
1372 | 29 | line = elements[-1] | 34 | line = elements[-1] |
1373 | 30 | references, value = line.rsplit('\0', 1) | 35 | references, value = line.rsplit('\0', 1) |
1374 | 31 | if ref_list_length: | 36 | if ref_list_length: |
1375 | 32 | ref_lists = [] | 37 | ref_lists = [] |
1376 | 33 | for ref_string in references.split('\t'): | 38 | for ref_string in references.split('\t'): |
1382 | 34 | ref_lists.append(tuple([ | 39 | ref_list = as_st([as_st(ref.split('\0')).intern() |
1383 | 35 | tuple(ref.split('\0')) for ref in ref_string.split('\r') if ref | 40 | for ref in ref_string.split('\r') if ref]) |
1384 | 36 | ])) | 41 | ref_lists.append(ref_list) |
1385 | 37 | ref_lists = tuple(ref_lists) | 42 | ref_lists = as_st(ref_lists) |
1386 | 38 | node_value = (value, ref_lists) | 43 | node_value = stuple(value, ref_lists) |
1387 | 39 | else: | 44 | else: |
1389 | 40 | node_value = (value, ()) | 45 | node_value = stuple(value, stuple()) |
1390 | 46 | # No need for StaticTuple here as it is put into a dict | ||
1391 | 41 | nodes.append((key, node_value)) | 47 | nodes.append((key, node_value)) |
1392 | 42 | return nodes | 48 | return nodes |
1393 | 43 | 49 | ||
1394 | 44 | 50 | ||
1395 | === modified file 'bzrlib/_btree_serializer_pyx.pyx' | |||
1396 | --- bzrlib/_btree_serializer_pyx.pyx 2010-01-05 04:59:57 +0000 | |||
1397 | +++ bzrlib/_btree_serializer_pyx.pyx 2010-02-18 00:00:52 +0000 | |||
1398 | @@ -1,4 +1,4 @@ | |||
1400 | 1 | # Copyright (C) 2008, 2009 Canonical Ltd | 1 | # Copyright (C) 2008, 2009, 2010 Canonical Ltd |
1401 | 2 | # | 2 | # |
1402 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
1403 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
1404 | @@ -38,11 +38,16 @@ | |||
1405 | 38 | Py_ssize_t PyString_Size(object p) | 38 | Py_ssize_t PyString_Size(object p) |
1406 | 39 | Py_ssize_t PyString_GET_SIZE_ptr "PyString_GET_SIZE" (PyObject *) | 39 | Py_ssize_t PyString_GET_SIZE_ptr "PyString_GET_SIZE" (PyObject *) |
1407 | 40 | char * PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *) | 40 | char * PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *) |
1408 | 41 | char * PyString_AS_STRING(object) | ||
1409 | 42 | Py_ssize_t PyString_GET_SIZE(object) | ||
1410 | 41 | int PyString_AsStringAndSize_ptr(PyObject *, char **buf, Py_ssize_t *len) | 43 | int PyString_AsStringAndSize_ptr(PyObject *, char **buf, Py_ssize_t *len) |
1411 | 42 | void PyString_InternInPlace(PyObject **) | 44 | void PyString_InternInPlace(PyObject **) |
1412 | 43 | int PyTuple_CheckExact(object t) | 45 | int PyTuple_CheckExact(object t) |
1413 | 46 | object PyTuple_New(Py_ssize_t n_entries) | ||
1414 | 47 | void PyTuple_SET_ITEM(object, Py_ssize_t offset, object) # steals the ref | ||
1415 | 44 | Py_ssize_t PyTuple_GET_SIZE(object t) | 48 | Py_ssize_t PyTuple_GET_SIZE(object t) |
1416 | 45 | PyObject *PyTuple_GET_ITEM_ptr_object "PyTuple_GET_ITEM" (object tpl, int index) | 49 | PyObject *PyTuple_GET_ITEM_ptr_object "PyTuple_GET_ITEM" (object tpl, int index) |
1417 | 50 | void Py_INCREF(object) | ||
1418 | 46 | void Py_DECREF_ptr "Py_DECREF" (PyObject *) | 51 | void Py_DECREF_ptr "Py_DECREF" (PyObject *) |
1419 | 47 | 52 | ||
1420 | 48 | cdef extern from "string.h": | 53 | cdef extern from "string.h": |
1421 | @@ -52,6 +57,12 @@ | |||
1422 | 52 | # void *memrchr(void *s, int c, size_t n) | 57 | # void *memrchr(void *s, int c, size_t n) |
1423 | 53 | int strncmp(char *s1, char *s2, size_t n) | 58 | int strncmp(char *s1, char *s2, size_t n) |
1424 | 54 | 59 | ||
1425 | 60 | # It seems we need to import the definitions so that the pyrex compiler has | ||
1426 | 61 | # local names to access them. | ||
1427 | 62 | from _static_tuple_c cimport StaticTuple, \ | ||
1428 | 63 | import_static_tuple_c, StaticTuple_New, \ | ||
1429 | 64 | StaticTuple_Intern, StaticTuple_SET_ITEM, StaticTuple_CheckExact | ||
1430 | 65 | |||
1431 | 55 | 66 | ||
1432 | 56 | # TODO: Find some way to import this from _dirstate_helpers | 67 | # TODO: Find some way to import this from _dirstate_helpers |
1433 | 57 | cdef void* _my_memrchr(void *s, int c, size_t n): # cannot_raise | 68 | cdef void* _my_memrchr(void *s, int c, size_t n): # cannot_raise |
1434 | @@ -68,6 +79,7 @@ | |||
1435 | 68 | pos = pos - 1 | 79 | pos = pos - 1 |
1436 | 69 | return NULL | 80 | return NULL |
1437 | 70 | 81 | ||
1438 | 82 | |||
1439 | 71 | # TODO: Import this from _dirstate_helpers when it is merged | 83 | # TODO: Import this from _dirstate_helpers when it is merged |
1440 | 72 | cdef object safe_string_from_size(char *s, Py_ssize_t size): | 84 | cdef object safe_string_from_size(char *s, Py_ssize_t size): |
1441 | 73 | if size < 0: | 85 | if size < 0: |
1442 | @@ -91,6 +103,10 @@ | |||
1443 | 91 | Py_DECREF_ptr(py_str) | 103 | Py_DECREF_ptr(py_str) |
1444 | 92 | return result | 104 | return result |
1445 | 93 | 105 | ||
1446 | 106 | from bzrlib import _static_tuple_c | ||
1447 | 107 | # This sets up the StaticTuple C_API functionality | ||
1448 | 108 | import_static_tuple_c() | ||
1449 | 109 | |||
1450 | 94 | 110 | ||
1451 | 95 | cdef class BTreeLeafParser: | 111 | cdef class BTreeLeafParser: |
1452 | 96 | """Parse the leaf nodes of a BTree index. | 112 | """Parse the leaf nodes of a BTree index. |
1453 | @@ -130,6 +146,7 @@ | |||
1454 | 130 | self._cur_str = NULL | 146 | self._cur_str = NULL |
1455 | 131 | self._end_str = NULL | 147 | self._end_str = NULL |
1456 | 132 | self._header_found = 0 | 148 | self._header_found = 0 |
1457 | 149 | # keys are tuples | ||
1458 | 133 | 150 | ||
1459 | 134 | cdef extract_key(self, char * last): | 151 | cdef extract_key(self, char * last): |
1460 | 135 | """Extract a key. | 152 | """Extract a key. |
1461 | @@ -139,15 +156,14 @@ | |||
1462 | 139 | """ | 156 | """ |
1463 | 140 | cdef char *temp_ptr | 157 | cdef char *temp_ptr |
1464 | 141 | cdef int loop_counter | 158 | cdef int loop_counter |
1470 | 142 | # keys are tuples | 159 | cdef StaticTuple key |
1471 | 143 | loop_counter = 0 | 160 | |
1472 | 144 | key_segments = [] | 161 | key = StaticTuple_New(self.key_length) |
1473 | 145 | while loop_counter < self.key_length: | 162 | for loop_counter from 0 <= loop_counter < self.key_length: |
1469 | 146 | loop_counter = loop_counter + 1 | ||
1474 | 147 | # grab a key segment | 163 | # grab a key segment |
1475 | 148 | temp_ptr = <char*>memchr(self._start, c'\0', last - self._start) | 164 | temp_ptr = <char*>memchr(self._start, c'\0', last - self._start) |
1476 | 149 | if temp_ptr == NULL: | 165 | if temp_ptr == NULL: |
1478 | 150 | if loop_counter == self.key_length: | 166 | if loop_counter + 1 == self.key_length: |
1479 | 151 | # capture to last | 167 | # capture to last |
1480 | 152 | temp_ptr = last | 168 | temp_ptr = last |
1481 | 153 | else: | 169 | else: |
1482 | @@ -157,15 +173,20 @@ | |||
1483 | 157 | last - self._start))) | 173 | last - self._start))) |
1484 | 158 | raise AssertionError(failure_string) | 174 | raise AssertionError(failure_string) |
1485 | 159 | # capture the key string | 175 | # capture the key string |
1490 | 160 | # TODO: Consider using PyIntern_FromString, the only caveat is that | 176 | if (self.key_length == 1 |
1491 | 161 | # it assumes a NULL-terminated string, so we have to check if | 177 | and (temp_ptr - self._start) == 45 |
1492 | 162 | # temp_ptr[0] == c'\0' or some other char. | 178 | and strncmp(self._start, 'sha1:', 5) == 0): |
1493 | 163 | key_element = safe_interned_string_from_size(self._start, | 179 | key_element = safe_string_from_size(self._start, |
1494 | 180 | temp_ptr - self._start) | ||
1495 | 181 | else: | ||
1496 | 182 | key_element = safe_interned_string_from_size(self._start, | ||
1497 | 164 | temp_ptr - self._start) | 183 | temp_ptr - self._start) |
1498 | 165 | # advance our pointer | 184 | # advance our pointer |
1499 | 166 | self._start = temp_ptr + 1 | 185 | self._start = temp_ptr + 1 |
1502 | 167 | PyList_Append(key_segments, key_element) | 186 | Py_INCREF(key_element) |
1503 | 168 | return tuple(key_segments) | 187 | StaticTuple_SET_ITEM(key, loop_counter, key_element) |
1504 | 188 | key = StaticTuple_Intern(key) | ||
1505 | 189 | return key | ||
1506 | 169 | 190 | ||
1507 | 170 | cdef int process_line(self) except -1: | 191 | cdef int process_line(self) except -1: |
1508 | 171 | """Process a line in the bytes.""" | 192 | """Process a line in the bytes.""" |
1509 | @@ -174,6 +195,7 @@ | |||
1510 | 174 | cdef char *ref_ptr | 195 | cdef char *ref_ptr |
1511 | 175 | cdef char *next_start | 196 | cdef char *next_start |
1512 | 176 | cdef int loop_counter | 197 | cdef int loop_counter |
1513 | 198 | cdef Py_ssize_t str_len | ||
1514 | 177 | 199 | ||
1515 | 178 | self._start = self._cur_str | 200 | self._start = self._cur_str |
1516 | 179 | # Find the next newline | 201 | # Find the next newline |
1517 | @@ -209,12 +231,25 @@ | |||
1518 | 209 | # Invalid line | 231 | # Invalid line |
1519 | 210 | raise AssertionError("Failed to find the value area") | 232 | raise AssertionError("Failed to find the value area") |
1520 | 211 | else: | 233 | else: |
1523 | 212 | # capture the value string | 234 | # Because of how conversions were done, we ended up with *lots* of |
1524 | 213 | value = safe_string_from_size(temp_ptr + 1, last - temp_ptr - 1) | 235 | # values that are identical. These are all of the 0-length nodes |
1525 | 236 | # that are referred to by the TREE_ROOT (and likely some other | ||
1526 | 237 | # directory nodes.) For example, bzr has 25k references to | ||
1527 | 238 | # something like '12607215 328306 0 0', which ends up consuming 1MB | ||
1528 | 239 | # of memory, just for those strings. | ||
1529 | 240 | str_len = last - temp_ptr - 1 | ||
1530 | 241 | if (str_len > 4 | ||
1531 | 242 | and strncmp(" 0 0", last - 4, 4) == 0): | ||
1532 | 243 | # This drops peak mem for bzr.dev from 87.4MB => 86.2MB | ||
1533 | 244 | # For Launchpad 236MB => 232MB | ||
1534 | 245 | value = safe_interned_string_from_size(temp_ptr + 1, str_len) | ||
1535 | 246 | else: | ||
1536 | 247 | value = safe_string_from_size(temp_ptr + 1, str_len) | ||
1537 | 214 | # shrink the references end point | 248 | # shrink the references end point |
1538 | 215 | last = temp_ptr | 249 | last = temp_ptr |
1539 | 250 | |||
1540 | 216 | if self.ref_list_length: | 251 | if self.ref_list_length: |
1542 | 217 | ref_lists = [] | 252 | ref_lists = StaticTuple_New(self.ref_list_length) |
1543 | 218 | loop_counter = 0 | 253 | loop_counter = 0 |
1544 | 219 | while loop_counter < self.ref_list_length: | 254 | while loop_counter < self.ref_list_length: |
1545 | 220 | ref_list = [] | 255 | ref_list = [] |
1546 | @@ -246,18 +281,20 @@ | |||
1547 | 246 | if temp_ptr == NULL: | 281 | if temp_ptr == NULL: |
1548 | 247 | # key runs to the end | 282 | # key runs to the end |
1549 | 248 | temp_ptr = ref_ptr | 283 | temp_ptr = ref_ptr |
1550 | 284 | |||
1551 | 249 | PyList_Append(ref_list, self.extract_key(temp_ptr)) | 285 | PyList_Append(ref_list, self.extract_key(temp_ptr)) |
1553 | 250 | PyList_Append(ref_lists, tuple(ref_list)) | 286 | ref_list = StaticTuple_Intern(StaticTuple(*ref_list)) |
1554 | 287 | Py_INCREF(ref_list) | ||
1555 | 288 | StaticTuple_SET_ITEM(ref_lists, loop_counter - 1, ref_list) | ||
1556 | 251 | # prepare for the next reference list | 289 | # prepare for the next reference list |
1557 | 252 | self._start = next_start | 290 | self._start = next_start |
1560 | 253 | ref_lists = tuple(ref_lists) | 291 | node_value = StaticTuple(value, ref_lists) |
1559 | 254 | node_value = (value, ref_lists) | ||
1561 | 255 | else: | 292 | else: |
1562 | 256 | if last != self._start: | 293 | if last != self._start: |
1563 | 257 | # unexpected reference data present | 294 | # unexpected reference data present |
1564 | 258 | raise AssertionError("unexpected reference data present") | 295 | raise AssertionError("unexpected reference data present") |
1567 | 259 | node_value = (value, ()) | 296 | node_value = StaticTuple(value, StaticTuple()) |
1568 | 260 | PyList_Append(self.keys, (key, node_value)) | 297 | PyList_Append(self.keys, StaticTuple(key, node_value)) |
1569 | 261 | return 0 | 298 | return 0 |
1570 | 262 | 299 | ||
1571 | 263 | def parse(self): | 300 | def parse(self): |
1572 | @@ -292,7 +329,6 @@ | |||
1573 | 292 | cdef Py_ssize_t flat_len | 329 | cdef Py_ssize_t flat_len |
1574 | 293 | cdef Py_ssize_t key_len | 330 | cdef Py_ssize_t key_len |
1575 | 294 | cdef Py_ssize_t node_len | 331 | cdef Py_ssize_t node_len |
1576 | 295 | cdef PyObject * val | ||
1577 | 296 | cdef char * value | 332 | cdef char * value |
1578 | 297 | cdef Py_ssize_t value_len | 333 | cdef Py_ssize_t value_len |
1579 | 298 | cdef char * out | 334 | cdef char * out |
1580 | @@ -301,13 +337,12 @@ | |||
1581 | 301 | cdef int first_ref_list | 337 | cdef int first_ref_list |
1582 | 302 | cdef int first_reference | 338 | cdef int first_reference |
1583 | 303 | cdef int i | 339 | cdef int i |
1584 | 304 | cdef PyObject *ref_bit | ||
1585 | 305 | cdef Py_ssize_t ref_bit_len | 340 | cdef Py_ssize_t ref_bit_len |
1586 | 306 | 341 | ||
1589 | 307 | if not PyTuple_CheckExact(node): | 342 | if not PyTuple_CheckExact(node) and not StaticTuple_CheckExact(node): |
1590 | 308 | raise TypeError('We expected a tuple() for node not: %s' | 343 | raise TypeError('We expected a tuple() or StaticTuple() for node not: %s' |
1591 | 309 | % type(node)) | 344 | % type(node)) |
1593 | 310 | node_len = PyTuple_GET_SIZE(node) | 345 | node_len = len(node) |
1594 | 311 | have_reference_lists = reference_lists | 346 | have_reference_lists = reference_lists |
1595 | 312 | if have_reference_lists: | 347 | if have_reference_lists: |
1596 | 313 | if node_len != 4: | 348 | if node_len != 4: |
1597 | @@ -316,8 +351,17 @@ | |||
1598 | 316 | elif node_len < 3: | 351 | elif node_len < 3: |
1599 | 317 | raise ValueError('Without ref_lists, we need at least 3 entries not: %s' | 352 | raise ValueError('Without ref_lists, we need at least 3 entries not: %s' |
1600 | 318 | % len(node)) | 353 | % len(node)) |
1603 | 319 | # I don't expect that we can do faster than string.join() | 354 | # TODO: We can probably do better than string.join(), namely |
1604 | 320 | string_key = '\0'.join(<object>PyTuple_GET_ITEM_ptr_object(node, 1)) | 355 | # when key has only 1 item, we can just grab that string |
1605 | 356 | # And when there are 2 items, we could do a single malloc + len() + 1 | ||
1606 | 357 | # also, doing .join() requires a PyObject_GetAttrString call, which | ||
1607 | 358 | # we could also avoid. | ||
1608 | 359 | # TODO: Note that pyrex 0.9.6 generates fairly crummy code here, using the | ||
1609 | 360 | # python object interface, versus 0.9.8+ which uses a helper that | ||
1610 | 361 | # checks if this supports the sequence interface. | ||
1611 | 362 | # We *could* do more work on our own, and grab the actual items | ||
1612 | 363 | # lists. For now, just ask people to use a better compiler. :) | ||
1613 | 364 | string_key = '\0'.join(node[1]) | ||
1614 | 321 | 365 | ||
1615 | 322 | # TODO: instead of using string joins, precompute the final string length, | 366 | # TODO: instead of using string joins, precompute the final string length, |
1616 | 323 | # and then malloc a single string and copy everything in. | 367 | # and then malloc a single string and copy everything in. |
1617 | @@ -334,7 +378,7 @@ | |||
1618 | 334 | refs_len = 0 | 378 | refs_len = 0 |
1619 | 335 | if have_reference_lists: | 379 | if have_reference_lists: |
1620 | 336 | # Figure out how many bytes it will take to store the references | 380 | # Figure out how many bytes it will take to store the references |
1622 | 337 | ref_lists = <object>PyTuple_GET_ITEM_ptr_object(node, 3) | 381 | ref_lists = node[3] |
1623 | 338 | next_len = len(ref_lists) # TODO: use a Py function | 382 | next_len = len(ref_lists) # TODO: use a Py function |
1624 | 339 | if next_len > 0: | 383 | if next_len > 0: |
1625 | 340 | # If there are no nodes, we don't need to do any work | 384 | # If there are no nodes, we don't need to do any work |
1626 | @@ -348,31 +392,31 @@ | |||
1627 | 348 | # references | 392 | # references |
1628 | 349 | refs_len = refs_len + (next_len - 1) | 393 | refs_len = refs_len + (next_len - 1) |
1629 | 350 | for reference in ref_list: | 394 | for reference in ref_list: |
1631 | 351 | if not PyTuple_CheckExact(reference): | 395 | if (not PyTuple_CheckExact(reference) |
1632 | 396 | and not StaticTuple_CheckExact(reference)): | ||
1633 | 352 | raise TypeError( | 397 | raise TypeError( |
1634 | 353 | 'We expect references to be tuples not: %s' | 398 | 'We expect references to be tuples not: %s' |
1635 | 354 | % type(reference)) | 399 | % type(reference)) |
1637 | 355 | next_len = PyTuple_GET_SIZE(reference) | 400 | next_len = len(reference) |
1638 | 356 | if next_len > 0: | 401 | if next_len > 0: |
1639 | 357 | # We will need (len - 1) '\x00' characters to | 402 | # We will need (len - 1) '\x00' characters to |
1640 | 358 | # separate the reference key | 403 | # separate the reference key |
1641 | 359 | refs_len = refs_len + (next_len - 1) | 404 | refs_len = refs_len + (next_len - 1) |
1645 | 360 | for i from 0 <= i < next_len: | 405 | for ref_bit in reference: |
1646 | 361 | ref_bit = PyTuple_GET_ITEM_ptr_object(reference, i) | 406 | if not PyString_CheckExact(ref_bit): |
1644 | 362 | if not PyString_CheckExact_ptr(ref_bit): | ||
1647 | 363 | raise TypeError('We expect reference bits' | 407 | raise TypeError('We expect reference bits' |
1648 | 364 | ' to be strings not: %s' | 408 | ' to be strings not: %s' |
1649 | 365 | % type(<object>ref_bit)) | 409 | % type(<object>ref_bit)) |
1651 | 366 | refs_len = refs_len + PyString_GET_SIZE_ptr(ref_bit) | 410 | refs_len = refs_len + PyString_GET_SIZE(ref_bit) |
1652 | 367 | 411 | ||
1653 | 368 | # So we have the (key NULL refs NULL value LF) | 412 | # So we have the (key NULL refs NULL value LF) |
1654 | 369 | key_len = PyString_Size(string_key) | 413 | key_len = PyString_Size(string_key) |
1657 | 370 | val = PyTuple_GET_ITEM_ptr_object(node, 2) | 414 | val = node[2] |
1658 | 371 | if not PyString_CheckExact_ptr(val): | 415 | if not PyString_CheckExact(val): |
1659 | 372 | raise TypeError('Expected a plain str for value not: %s' | 416 | raise TypeError('Expected a plain str for value not: %s' |
1663 | 373 | % type(<object>val)) | 417 | % type(val)) |
1664 | 374 | value = PyString_AS_STRING_ptr(val) | 418 | value = PyString_AS_STRING(val) |
1665 | 375 | value_len = PyString_GET_SIZE_ptr(val) | 419 | value_len = PyString_GET_SIZE(val) |
1666 | 376 | flat_len = (key_len + 1 + refs_len + 1 + value_len + 1) | 420 | flat_len = (key_len + 1 + refs_len + 1 + value_len + 1) |
1667 | 377 | line = PyString_FromStringAndSize(NULL, flat_len) | 421 | line = PyString_FromStringAndSize(NULL, flat_len) |
1668 | 378 | # Get a pointer to the new buffer | 422 | # Get a pointer to the new buffer |
1669 | @@ -394,14 +438,14 @@ | |||
1670 | 394 | out[0] = c'\r' | 438 | out[0] = c'\r' |
1671 | 395 | out = out + 1 | 439 | out = out + 1 |
1672 | 396 | first_reference = 0 | 440 | first_reference = 0 |
1674 | 397 | next_len = PyTuple_GET_SIZE(reference) | 441 | next_len = len(reference) |
1675 | 398 | for i from 0 <= i < next_len: | 442 | for i from 0 <= i < next_len: |
1676 | 399 | if i != 0: | 443 | if i != 0: |
1677 | 400 | out[0] = c'\x00' | 444 | out[0] = c'\x00' |
1678 | 401 | out = out + 1 | 445 | out = out + 1 |
1682 | 402 | ref_bit = PyTuple_GET_ITEM_ptr_object(reference, i) | 446 | ref_bit = reference[i] |
1683 | 403 | ref_bit_len = PyString_GET_SIZE_ptr(ref_bit) | 447 | ref_bit_len = PyString_GET_SIZE(ref_bit) |
1684 | 404 | memcpy(out, PyString_AS_STRING_ptr(ref_bit), ref_bit_len) | 448 | memcpy(out, PyString_AS_STRING(ref_bit), ref_bit_len) |
1685 | 405 | out = out + ref_bit_len | 449 | out = out + ref_bit_len |
1686 | 406 | out[0] = c'\0' | 450 | out[0] = c'\0' |
1687 | 407 | out = out + 1 | 451 | out = out + 1 |
1688 | 408 | 452 | ||
1689 | === modified file 'bzrlib/_chk_map_py.py' | |||
1690 | --- bzrlib/_chk_map_py.py 2009-04-09 20:23:07 +0000 | |||
1691 | +++ bzrlib/_chk_map_py.py 2010-02-18 00:00:52 +0000 | |||
1692 | @@ -1,4 +1,4 @@ | |||
1694 | 1 | # Copyright (C) 2009 Canonical Ltd | 1 | # Copyright (C) 2009, 2010 Canonical Ltd |
1695 | 2 | # | 2 | # |
1696 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
1697 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
1698 | @@ -19,6 +19,8 @@ | |||
1699 | 19 | import zlib | 19 | import zlib |
1700 | 20 | import struct | 20 | import struct |
1701 | 21 | 21 | ||
1702 | 22 | from bzrlib.static_tuple import StaticTuple | ||
1703 | 23 | |||
1704 | 22 | _LeafNode = None | 24 | _LeafNode = None |
1705 | 23 | _InternalNode = None | 25 | _InternalNode = None |
1706 | 24 | _unknown = None | 26 | _unknown = None |
1707 | @@ -93,7 +95,7 @@ | |||
1708 | 93 | value_lines = lines[pos:pos+num_value_lines] | 95 | value_lines = lines[pos:pos+num_value_lines] |
1709 | 94 | pos += num_value_lines | 96 | pos += num_value_lines |
1710 | 95 | value = '\n'.join(value_lines) | 97 | value = '\n'.join(value_lines) |
1712 | 96 | items[tuple(elements[:-1])] = value | 98 | items[StaticTuple.from_sequence(elements[:-1])] = value |
1713 | 97 | if len(items) != length: | 99 | if len(items) != length: |
1714 | 98 | raise AssertionError("item count (%d) mismatch for key %s," | 100 | raise AssertionError("item count (%d) mismatch for key %s," |
1715 | 99 | " bytes %r" % (length, key, bytes)) | 101 | " bytes %r" % (length, key, bytes)) |
1716 | @@ -141,7 +143,7 @@ | |||
1717 | 141 | for line in lines[5:]: | 143 | for line in lines[5:]: |
1718 | 142 | line = common_prefix + line | 144 | line = common_prefix + line |
1719 | 143 | prefix, flat_key = line.rsplit('\x00', 1) | 145 | prefix, flat_key = line.rsplit('\x00', 1) |
1721 | 144 | items[prefix] = (flat_key,) | 146 | items[prefix] = StaticTuple(flat_key,) |
1722 | 145 | if len(items) == 0: | 147 | if len(items) == 0: |
1723 | 146 | raise AssertionError("We didn't find any item for %s" % key) | 148 | raise AssertionError("We didn't find any item for %s" % key) |
1724 | 147 | result._items = items | 149 | result._items = items |
1725 | @@ -155,4 +157,3 @@ | |||
1726 | 155 | result._node_width = len(prefix) | 157 | result._node_width = len(prefix) |
1727 | 156 | result._search_prefix = common_prefix | 158 | result._search_prefix = common_prefix |
1728 | 157 | return result | 159 | return result |
1729 | 158 | |||
1730 | 159 | 160 | ||
1731 | === modified file 'bzrlib/_chk_map_pyx.pyx' | |||
1732 | --- bzrlib/_chk_map_pyx.pyx 2010-01-05 04:59:57 +0000 | |||
1733 | +++ bzrlib/_chk_map_pyx.pyx 2010-02-18 00:00:51 +0000 | |||
1734 | @@ -1,4 +1,4 @@ | |||
1736 | 1 | # Copyright (C) 2009 Canonical Ltd | 1 | # Copyright (C) 2009, 2010 Canonical Ltd |
1737 | 2 | # | 2 | # |
1738 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
1739 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
1740 | @@ -29,9 +29,8 @@ | |||
1741 | 29 | 29 | ||
1742 | 30 | cdef extern from "Python.h": | 30 | cdef extern from "Python.h": |
1743 | 31 | ctypedef int Py_ssize_t # Required for older pyrex versions | 31 | ctypedef int Py_ssize_t # Required for older pyrex versions |
1745 | 32 | struct _PyObject: | 32 | ctypedef struct PyObject: |
1746 | 33 | pass | 33 | pass |
1747 | 34 | ctypedef _PyObject PyObject | ||
1748 | 35 | int PyTuple_CheckExact(object p) | 34 | int PyTuple_CheckExact(object p) |
1749 | 36 | Py_ssize_t PyTuple_GET_SIZE(object t) | 35 | Py_ssize_t PyTuple_GET_SIZE(object t) |
1750 | 37 | int PyString_CheckExact(object) | 36 | int PyString_CheckExact(object) |
1751 | @@ -52,6 +51,18 @@ | |||
1752 | 52 | char *PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *s) | 51 | char *PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *s) |
1753 | 53 | object PyString_FromStringAndSize(char*, Py_ssize_t) | 52 | object PyString_FromStringAndSize(char*, Py_ssize_t) |
1754 | 54 | 53 | ||
1755 | 54 | # cimport all of the definitions we will need to access | ||
1756 | 55 | from _static_tuple_c cimport StaticTuple,\ | ||
1757 | 56 | import_static_tuple_c, StaticTuple_New, \ | ||
1758 | 57 | StaticTuple_Intern, StaticTuple_SET_ITEM, StaticTuple_CheckExact | ||
1759 | 58 | |||
1760 | 59 | cdef extern from "_static_tuple_c.h": | ||
1761 | 60 | # Defined explicitly rather than cimport-ing. Trying to use cimport, the | ||
1762 | 61 | # type for PyObject is a different class that happens to have the same | ||
1763 | 62 | # name... | ||
1764 | 63 | PyObject * StaticTuple_GET_ITEM_ptr "StaticTuple_GET_ITEM" (StaticTuple, | ||
1765 | 64 | Py_ssize_t) | ||
1766 | 65 | |||
1767 | 55 | cdef extern from "zlib.h": | 66 | cdef extern from "zlib.h": |
1768 | 56 | ctypedef unsigned long uLong | 67 | ctypedef unsigned long uLong |
1769 | 57 | ctypedef unsigned int uInt | 68 | ctypedef unsigned int uInt |
1770 | @@ -60,8 +71,14 @@ | |||
1771 | 60 | uLong crc32(uLong crc, Bytef *buf, uInt len) | 71 | uLong crc32(uLong crc, Bytef *buf, uInt len) |
1772 | 61 | 72 | ||
1773 | 62 | 73 | ||
1774 | 74 | # Set up the StaticTuple C_API functionality | ||
1775 | 75 | import_static_tuple_c() | ||
1776 | 76 | |||
1777 | 77 | cdef object _LeafNode | ||
1778 | 63 | _LeafNode = None | 78 | _LeafNode = None |
1779 | 79 | cdef object _InternalNode | ||
1780 | 64 | _InternalNode = None | 80 | _InternalNode = None |
1781 | 81 | cdef object _unknown | ||
1782 | 65 | _unknown = None | 82 | _unknown = None |
1783 | 66 | 83 | ||
1784 | 67 | # We shouldn't just copy this from _dirstate_helpers_pyx | 84 | # We shouldn't just copy this from _dirstate_helpers_pyx |
1785 | @@ -91,9 +108,9 @@ | |||
1786 | 91 | cdef char *c_out | 108 | cdef char *c_out |
1787 | 92 | cdef PyObject *bit | 109 | cdef PyObject *bit |
1788 | 93 | 110 | ||
1792 | 94 | if not PyTuple_CheckExact(key): | 111 | if not StaticTuple_CheckExact(key): |
1793 | 95 | raise TypeError('key %r is not a tuple' % (key,)) | 112 | raise TypeError('key %r is not a StaticTuple' % (key,)) |
1794 | 96 | num_bits = PyTuple_GET_SIZE(key) | 113 | num_bits = len(key) |
1795 | 97 | # 4 bytes per crc32, and another 1 byte between bits | 114 | # 4 bytes per crc32, and another 1 byte between bits |
1796 | 98 | num_out_bytes = (9 * num_bits) - 1 | 115 | num_out_bytes = (9 * num_bits) - 1 |
1797 | 99 | out = PyString_FromStringAndSize(NULL, num_out_bytes) | 116 | out = PyString_FromStringAndSize(NULL, num_out_bytes) |
1798 | @@ -105,7 +122,7 @@ | |||
1799 | 105 | # We use the _ptr variant, because GET_ITEM returns a borrowed | 122 | # We use the _ptr variant, because GET_ITEM returns a borrowed |
1800 | 106 | # reference, and Pyrex assumes that returned 'object' are a new | 123 | # reference, and Pyrex assumes that returned 'object' are a new |
1801 | 107 | # reference | 124 | # reference |
1803 | 108 | bit = PyTuple_GET_ITEM_ptr(key, i) | 125 | bit = StaticTuple_GET_ITEM_ptr(key, i) |
1804 | 109 | if not PyString_CheckExact_ptr(bit): | 126 | if not PyString_CheckExact_ptr(bit): |
1805 | 110 | raise TypeError('Bit %d of %r is not a string' % (i, key)) | 127 | raise TypeError('Bit %d of %r is not a string' % (i, key)) |
1806 | 111 | c_bit = <Bytef *>PyString_AS_STRING_ptr(bit) | 128 | c_bit = <Bytef *>PyString_AS_STRING_ptr(bit) |
1807 | @@ -129,9 +146,9 @@ | |||
1808 | 129 | cdef char *c_out | 146 | cdef char *c_out |
1809 | 130 | cdef PyObject *bit | 147 | cdef PyObject *bit |
1810 | 131 | 148 | ||
1814 | 132 | if not PyTuple_CheckExact(key): | 149 | if not StaticTuple_CheckExact(key): |
1815 | 133 | raise TypeError('key %r is not a tuple' % (key,)) | 150 | raise TypeError('key %r is not a StaticTuple' % (key,)) |
1816 | 134 | num_bits = PyTuple_GET_SIZE(key) | 151 | num_bits = len(key) |
1817 | 135 | # 4 bytes per crc32, and another 1 byte between bits | 152 | # 4 bytes per crc32, and another 1 byte between bits |
1818 | 136 | num_out_bytes = (5 * num_bits) - 1 | 153 | num_out_bytes = (5 * num_bits) - 1 |
1819 | 137 | out = PyString_FromStringAndSize(NULL, num_out_bytes) | 154 | out = PyString_FromStringAndSize(NULL, num_out_bytes) |
1820 | @@ -140,10 +157,10 @@ | |||
1821 | 140 | if i > 0: | 157 | if i > 0: |
1822 | 141 | c_out[0] = c'\x00' | 158 | c_out[0] = c'\x00' |
1823 | 142 | c_out = c_out + 1 | 159 | c_out = c_out + 1 |
1825 | 143 | bit = PyTuple_GET_ITEM_ptr(key, i) | 160 | bit = StaticTuple_GET_ITEM_ptr(key, i) |
1826 | 144 | if not PyString_CheckExact_ptr(bit): | 161 | if not PyString_CheckExact_ptr(bit): |
1829 | 145 | raise TypeError('Bit %d of %r is not a string: %r' % (i, key, | 162 | raise TypeError('Bit %d of %r is not a string: %r' |
1830 | 146 | <object>bit)) | 163 | % (i, key, <object>bit)) |
1831 | 147 | c_bit = <Bytef *>PyString_AS_STRING_ptr(bit) | 164 | c_bit = <Bytef *>PyString_AS_STRING_ptr(bit) |
1832 | 148 | c_len = PyString_GET_SIZE_ptr(bit) | 165 | c_len = PyString_GET_SIZE_ptr(bit) |
1833 | 149 | crc_val = crc32(0, c_bit, c_len) | 166 | crc_val = crc32(0, c_bit, c_len) |
1834 | @@ -195,6 +212,7 @@ | |||
1835 | 195 | cdef char *prefix, *value_start, *prefix_tail | 212 | cdef char *prefix, *value_start, *prefix_tail |
1836 | 196 | cdef char *next_null, *last_null, *line_start | 213 | cdef char *next_null, *last_null, *line_start |
1837 | 197 | cdef char *c_entry, *entry_start | 214 | cdef char *c_entry, *entry_start |
1838 | 215 | cdef StaticTuple entry_bits | ||
1839 | 198 | 216 | ||
1840 | 199 | if _LeafNode is None: | 217 | if _LeafNode is None: |
1841 | 200 | from bzrlib import chk_map | 218 | from bzrlib import chk_map |
1842 | @@ -265,12 +283,14 @@ | |||
1843 | 265 | if next_line == NULL: | 283 | if next_line == NULL: |
1844 | 266 | raise ValueError('missing trailing newline') | 284 | raise ValueError('missing trailing newline') |
1845 | 267 | cur = next_line + 1 | 285 | cur = next_line + 1 |
1847 | 268 | entry_bits = PyTuple_New(width) | 286 | entry_bits = StaticTuple_New(width) |
1848 | 269 | for i from 0 <= i < num_prefix_bits: | 287 | for i from 0 <= i < num_prefix_bits: |
1849 | 288 | # TODO: Use PyList_GetItem, or turn prefix_bits into a | ||
1850 | 289 | # tuple/StaticTuple | ||
1851 | 270 | entry = prefix_bits[i] | 290 | entry = prefix_bits[i] |
1852 | 271 | # SET_ITEM 'steals' a reference | 291 | # SET_ITEM 'steals' a reference |
1853 | 272 | Py_INCREF(entry) | 292 | Py_INCREF(entry) |
1855 | 273 | PyTuple_SET_ITEM(entry_bits, i, entry) | 293 | StaticTuple_SET_ITEM(entry_bits, i, entry) |
1856 | 274 | value = PyString_FromStringAndSize(value_start, next_line - value_start) | 294 | value = PyString_FromStringAndSize(value_start, next_line - value_start) |
1857 | 275 | # The next entry bit needs the 'tail' from the prefix, and first part | 295 | # The next entry bit needs the 'tail' from the prefix, and first part |
1858 | 276 | # of the line | 296 | # of the line |
1859 | @@ -288,7 +308,7 @@ | |||
1860 | 288 | memcpy(c_entry + prefix_tail_len, line_start, next_null - line_start) | 308 | memcpy(c_entry + prefix_tail_len, line_start, next_null - line_start) |
1861 | 289 | Py_INCREF(entry) | 309 | Py_INCREF(entry) |
1862 | 290 | i = num_prefix_bits | 310 | i = num_prefix_bits |
1864 | 291 | PyTuple_SET_ITEM(entry_bits, i, entry) | 311 | StaticTuple_SET_ITEM(entry_bits, i, entry) |
1865 | 292 | while next_null != last_null: # We have remaining bits | 312 | while next_null != last_null: # We have remaining bits |
1866 | 293 | i = i + 1 | 313 | i = i + 1 |
1867 | 294 | if i > width: | 314 | if i > width: |
1868 | @@ -301,11 +321,12 @@ | |||
1869 | 301 | entry = PyString_FromStringAndSize(entry_start, | 321 | entry = PyString_FromStringAndSize(entry_start, |
1870 | 302 | next_null - entry_start) | 322 | next_null - entry_start) |
1871 | 303 | Py_INCREF(entry) | 323 | Py_INCREF(entry) |
1873 | 304 | PyTuple_SET_ITEM(entry_bits, i, entry) | 324 | StaticTuple_SET_ITEM(entry_bits, i, entry) |
1874 | 305 | if len(entry_bits) != width: | 325 | if len(entry_bits) != width: |
1875 | 306 | raise AssertionError( | 326 | raise AssertionError( |
1876 | 307 | 'Incorrect number of elements (%d vs %d)' | 327 | 'Incorrect number of elements (%d vs %d)' |
1877 | 308 | % (len(entry_bits)+1, width + 1)) | 328 | % (len(entry_bits)+1, width + 1)) |
1878 | 329 | entry_bits = StaticTuple_Intern(entry_bits) | ||
1879 | 309 | PyDict_SetItem(items, entry_bits, value) | 330 | PyDict_SetItem(items, entry_bits, value) |
1880 | 310 | if len(items) != length: | 331 | if len(items) != length: |
1881 | 311 | raise ValueError("item count (%d) mismatch for key %s," | 332 | raise ValueError("item count (%d) mismatch for key %s," |
1882 | @@ -343,6 +364,8 @@ | |||
1883 | 343 | _unknown = chk_map._unknown | 364 | _unknown = chk_map._unknown |
1884 | 344 | result = _InternalNode(search_key_func=search_key_func) | 365 | result = _InternalNode(search_key_func=search_key_func) |
1885 | 345 | 366 | ||
1886 | 367 | if not StaticTuple_CheckExact(key): | ||
1887 | 368 | raise TypeError('key %r is not a StaticTuple' % (key,)) | ||
1888 | 346 | if not PyString_CheckExact(bytes): | 369 | if not PyString_CheckExact(bytes): |
1889 | 347 | raise TypeError('bytes must be a plain string not %s' % (type(bytes),)) | 370 | raise TypeError('bytes must be a plain string not %s' % (type(bytes),)) |
1890 | 348 | 371 | ||
1891 | @@ -384,7 +407,8 @@ | |||
1892 | 384 | memcpy(c_item_prefix + prefix_length, cur, next_null - cur) | 407 | memcpy(c_item_prefix + prefix_length, cur, next_null - cur) |
1893 | 385 | flat_key = PyString_FromStringAndSize(next_null + 1, | 408 | flat_key = PyString_FromStringAndSize(next_null + 1, |
1894 | 386 | next_line - next_null - 1) | 409 | next_line - next_null - 1) |
1896 | 387 | PyDict_SetItem(items, item_prefix, (flat_key,)) | 410 | flat_key = StaticTuple(flat_key).intern() |
1897 | 411 | PyDict_SetItem(items, item_prefix, flat_key) | ||
1898 | 388 | cur = next_line + 1 | 412 | cur = next_line + 1 |
1899 | 389 | assert len(items) > 0 | 413 | assert len(items) > 0 |
1900 | 390 | result._items = items | 414 | result._items = items |
1901 | @@ -398,4 +422,3 @@ | |||
1902 | 398 | result._node_width = len(item_prefix) | 422 | result._node_width = len(item_prefix) |
1903 | 399 | result._search_prefix = PyString_FromStringAndSize(prefix, prefix_length) | 423 | result._search_prefix = PyString_FromStringAndSize(prefix, prefix_length) |
1904 | 400 | return result | 424 | return result |
1905 | 401 | |||
1906 | 402 | 425 | ||
1907 | === modified file 'bzrlib/_dirstate_helpers_pyx.pyx' | |||
1908 | --- bzrlib/_dirstate_helpers_pyx.pyx 2010-01-05 04:59:57 +0000 | |||
1909 | +++ bzrlib/_dirstate_helpers_pyx.pyx 2010-02-18 00:00:51 +0000 | |||
1910 | @@ -1,4 +1,4 @@ | |||
1912 | 1 | # Copyright (C) 2007, 2008 Canonical Ltd | 1 | # Copyright (C) 2007-2010 Canonical Ltd |
1913 | 2 | # | 2 | # |
1914 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
1915 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
1916 | 5 | 5 | ||
1917 | === added file 'bzrlib/_export_c_api.h' | |||
1918 | --- bzrlib/_export_c_api.h 1970-01-01 00:00:00 +0000 | |||
1919 | +++ bzrlib/_export_c_api.h 2010-02-18 00:00:51 +0000 | |||
1920 | @@ -0,0 +1,104 @@ | |||
1921 | 1 | /* Copyright (C) 2009 Canonical Ltd | ||
1922 | 2 | * | ||
1923 | 3 | * This program is free software; you can redistribute it and/or modify | ||
1924 | 4 | * it under the terms of the GNU General Public License as published by | ||
1925 | 5 | * the Free Software Foundation; either version 2 of the License, or | ||
1926 | 6 | * (at your option) any later version. | ||
1927 | 7 | * | ||
1928 | 8 | * This program is distributed in the hope that it will be useful, | ||
1929 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1930 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1931 | 11 | * GNU General Public License for more details. | ||
1932 | 12 | * | ||
1933 | 13 | * You should have received a copy of the GNU General Public License | ||
1934 | 14 | * along with this program; if not, write to the Free Software | ||
1935 | 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
1936 | 16 | */ | ||
1937 | 17 | |||
1938 | 18 | |||
1939 | 19 | /* This file contains helper functions for exporting a C API for a CPython | ||
1940 | 20 | * extension module. | ||
1941 | 21 | */ | ||
1942 | 22 | |||
1943 | 23 | #ifndef _EXPORT_C_API_H_ | ||
1944 | 24 | #define _EXPORT_C_API_H_ | ||
1945 | 25 | |||
1946 | 26 | static const char *_C_API_NAME = "_C_API"; | ||
1947 | 27 | |||
1948 | 28 | /** | ||
1949 | 29 | * Add a C function to the modules _C_API | ||
1950 | 30 | * This wraps the function in a PyCObject, and inserts that into a dict. | ||
1951 | 31 | * The key of the dict is the function name, and the description is the | ||
1952 | 32 | * signature of the function. | ||
1953 | 33 | * This is generally called during a modules init_MODULE function. | ||
1954 | 34 | * | ||
1955 | 35 | * @param module A Python module (the one being initialized) | ||
1956 | 36 | * @param funcname The name of the function being exported | ||
1957 | 37 | * @param func A pointer to the function | ||
1958 | 38 | * @param signature The C signature of the function | ||
1959 | 39 | * @return 0 if everything is successful, -1 if there is a problem. An | ||
1960 | 40 | * exception should also be set | ||
1961 | 41 | */ | ||
1962 | 42 | static int | ||
1963 | 43 | _export_function(PyObject *module, char *funcname, void *func, char *signature) | ||
1964 | 44 | { | ||
1965 | 45 | PyObject *d = NULL; | ||
1966 | 46 | PyObject *c_obj = NULL; | ||
1967 | 47 | |||
1968 | 48 | /* (char *) is because python2.4 declares this api as 'char *' rather than | ||
1969 | 49 | * const char* which it really is. | ||
1970 | 50 | */ | ||
1971 | 51 | d = PyObject_GetAttrString(module, (char *)_C_API_NAME); | ||
1972 | 52 | if (!d) { | ||
1973 | 53 | PyErr_Clear(); | ||
1974 | 54 | d = PyDict_New(); | ||
1975 | 55 | if (!d) | ||
1976 | 56 | goto bad; | ||
1977 | 57 | Py_INCREF(d); | ||
1978 | 58 | if (PyModule_AddObject(module, (char *)_C_API_NAME, d) < 0) | ||
1979 | 59 | goto bad; | ||
1980 | 60 | } | ||
1981 | 61 | c_obj = PyCObject_FromVoidPtrAndDesc(func, signature, 0); | ||
1982 | 62 | if (!c_obj) | ||
1983 | 63 | goto bad; | ||
1984 | 64 | if (PyDict_SetItemString(d, funcname, c_obj) < 0) | ||
1985 | 65 | goto bad; | ||
1986 | 66 | Py_DECREF(d); | ||
1987 | 67 | return 0; | ||
1988 | 68 | bad: | ||
1989 | 69 | Py_XDECREF(c_obj); | ||
1990 | 70 | Py_XDECREF(d); | ||
1991 | 71 | return -1; | ||
1992 | 72 | } | ||
1993 | 73 | |||
1994 | 74 | /* Note: | ||
1995 | 75 | * It feels like more could be done here. Specifically, if you look at | ||
1996 | 76 | * _static_tuple_c.h you can see some boilerplate where we have: | ||
1997 | 77 | * #ifdef STATIC_TUPLE_MODULE // are we exporting or importing | ||
1998 | 78 | * static RETVAL FUNCNAME PROTO; | ||
1999 | 79 | * #else | ||
2000 | 80 | * static RETVAL (*FUNCNAME) PROTO; | ||
2001 | 81 | * #endif | ||
2002 | 82 | * | ||
2003 | 83 | * And then in _static_tuple_c.c we have | ||
2004 | 84 | * int setup_c_api() | ||
2005 | 85 | * { | ||
2006 | 86 | * _export_function(module, #FUNCNAME, FUNCNAME, #PROTO); | ||
2007 | 87 | * } | ||
2008 | 88 | * | ||
2009 | 89 | * And then in _static_tuple_c.h import_##MODULE | ||
2010 | 90 | * struct function_definition functions[] = { | ||
2011 | 91 | * {#FUNCNAME, (void **)&FUNCNAME, #RETVAL #PROTO}, | ||
2012 | 92 | * ... | ||
2013 | 93 | * {NULL}}; | ||
2014 | 94 | * | ||
2015 | 95 | * And some similar stuff for types. However, this would mean that we would | ||
2016 | 96 | * need a way for the C preprocessor to build up a list of definitions to be | ||
2017 | 97 | * generated, and then expand that list at the appropriate time. | ||
2018 | 98 | * I would guess there would be a way to do this, but probably not without a | ||
2019 | 99 | * lot of magic, and the end result probably wouldn't be very pretty to | ||
2020 | 100 | * maintain. Perhaps python's dynamic nature has left me jaded about writing | ||
2021 | 101 | * boilerplate.... | ||
2022 | 102 | */ | ||
2023 | 103 | |||
2024 | 104 | #endif // _EXPORT_C_API_H_ | ||
2025 | 0 | 105 | ||
2026 | === modified file 'bzrlib/_groupcompress_pyx.pyx' | |||
2027 | --- bzrlib/_groupcompress_pyx.pyx 2010-01-05 04:59:57 +0000 | |||
2028 | +++ bzrlib/_groupcompress_pyx.pyx 2010-02-18 00:00:52 +0000 | |||
2029 | @@ -1,4 +1,4 @@ | |||
2031 | 1 | # Copyright (C) 2009 Canonical Ltd | 1 | # Copyright (C) 2008, 2009, 2010 Canonical Ltd |
2032 | 2 | # | 2 | # |
2033 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
2034 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
2035 | @@ -31,10 +31,10 @@ | |||
2036 | 31 | 31 | ||
2037 | 32 | cdef extern from *: | 32 | cdef extern from *: |
2038 | 33 | ctypedef unsigned long size_t | 33 | ctypedef unsigned long size_t |
2043 | 34 | void * malloc(size_t) | 34 | void * malloc(size_t) nogil |
2044 | 35 | void * realloc(void *, size_t) | 35 | void * realloc(void *, size_t) nogil |
2045 | 36 | void free(void *) | 36 | void free(void *) nogil |
2046 | 37 | void memcpy(void *, void *, size_t) | 37 | void memcpy(void *, void *, size_t) nogil |
2047 | 38 | 38 | ||
2048 | 39 | 39 | ||
2049 | 40 | cdef extern from "delta.h": | 40 | cdef extern from "delta.h": |
2050 | @@ -44,19 +44,16 @@ | |||
2051 | 44 | unsigned long agg_offset | 44 | unsigned long agg_offset |
2052 | 45 | struct delta_index: | 45 | struct delta_index: |
2053 | 46 | pass | 46 | pass |
2055 | 47 | delta_index * create_delta_index(source_info *src, delta_index *old) | 47 | delta_index * create_delta_index(source_info *src, delta_index *old) nogil |
2056 | 48 | delta_index * create_delta_index_from_delta(source_info *delta, | 48 | delta_index * create_delta_index_from_delta(source_info *delta, |
2059 | 49 | delta_index *old) | 49 | delta_index *old) nogil |
2060 | 50 | void free_delta_index(delta_index *index) | 50 | void free_delta_index(delta_index *index) nogil |
2061 | 51 | void *create_delta(delta_index *indexes, | 51 | void *create_delta(delta_index *indexes, |
2062 | 52 | void *buf, unsigned long bufsize, | 52 | void *buf, unsigned long bufsize, |
2064 | 53 | unsigned long *delta_size, unsigned long max_delta_size) | 53 | unsigned long *delta_size, unsigned long max_delta_size) nogil |
2065 | 54 | unsigned long get_delta_hdr_size(unsigned char **datap, | 54 | unsigned long get_delta_hdr_size(unsigned char **datap, |
2067 | 55 | unsigned char *top) | 55 | unsigned char *top) nogil |
2068 | 56 | Py_ssize_t DELTA_SIZE_MIN | 56 | Py_ssize_t DELTA_SIZE_MIN |
2069 | 57 | void *patch_delta(void *src_buf, unsigned long src_size, | ||
2070 | 58 | void *delta_buf, unsigned long delta_size, | ||
2071 | 59 | unsigned long *dst_size) | ||
2072 | 60 | 57 | ||
2073 | 61 | 58 | ||
2074 | 62 | cdef void *safe_malloc(size_t count) except NULL: | 59 | cdef void *safe_malloc(size_t count) except NULL: |
2075 | @@ -148,7 +145,8 @@ | |||
2076 | 148 | src.buf = c_delta | 145 | src.buf = c_delta |
2077 | 149 | src.size = c_delta_size | 146 | src.size = c_delta_size |
2078 | 150 | src.agg_offset = self._source_offset + unadded_bytes | 147 | src.agg_offset = self._source_offset + unadded_bytes |
2080 | 151 | index = create_delta_index_from_delta(src, self._index) | 148 | with nogil: |
2081 | 149 | index = create_delta_index_from_delta(src, self._index) | ||
2082 | 152 | self._source_offset = src.agg_offset + src.size | 150 | self._source_offset = src.agg_offset + src.size |
2083 | 153 | if index != NULL: | 151 | if index != NULL: |
2084 | 154 | free_delta_index(self._index) | 152 | free_delta_index(self._index) |
2085 | @@ -188,7 +186,8 @@ | |||
2086 | 188 | self._source_offset = src.agg_offset + src.size | 186 | self._source_offset = src.agg_offset + src.size |
2087 | 189 | # We delay creating the index on the first insert | 187 | # We delay creating the index on the first insert |
2088 | 190 | if source_location != 0: | 188 | if source_location != 0: |
2090 | 191 | index = create_delta_index(src, self._index) | 189 | with nogil: |
2091 | 190 | index = create_delta_index(src, self._index) | ||
2092 | 192 | if index != NULL: | 191 | if index != NULL: |
2093 | 193 | free_delta_index(self._index) | 192 | free_delta_index(self._index) |
2094 | 194 | self._index = index | 193 | self._index = index |
2095 | @@ -201,7 +200,8 @@ | |||
2096 | 201 | 200 | ||
2097 | 202 | # We know that self._index is already NULL, so whatever | 201 | # We know that self._index is already NULL, so whatever |
2098 | 203 | # create_delta_index returns is fine | 202 | # create_delta_index returns is fine |
2100 | 204 | self._index = create_delta_index(&self._source_infos[0], NULL) | 203 | with nogil: |
2101 | 204 | self._index = create_delta_index(&self._source_infos[0], NULL) | ||
2102 | 205 | assert self._index != NULL | 205 | assert self._index != NULL |
2103 | 206 | 206 | ||
2104 | 207 | cdef _expand_sources(self): | 207 | cdef _expand_sources(self): |
2105 | @@ -218,6 +218,7 @@ | |||
2106 | 218 | cdef Py_ssize_t target_size | 218 | cdef Py_ssize_t target_size |
2107 | 219 | cdef void * delta | 219 | cdef void * delta |
2108 | 220 | cdef unsigned long delta_size | 220 | cdef unsigned long delta_size |
2109 | 221 | cdef unsigned long c_max_delta_size | ||
2110 | 221 | 222 | ||
2111 | 222 | if self._index == NULL: | 223 | if self._index == NULL: |
2112 | 223 | if len(self._sources) == 0: | 224 | if len(self._sources) == 0: |
2113 | @@ -234,9 +235,11 @@ | |||
2114 | 234 | # TODO: inline some of create_delta so we at least don't have to double | 235 | # TODO: inline some of create_delta so we at least don't have to double |
2115 | 235 | # malloc, and can instead use PyString_FromStringAndSize, to | 236 | # malloc, and can instead use PyString_FromStringAndSize, to |
2116 | 236 | # allocate the bytes into the final string | 237 | # allocate the bytes into the final string |
2120 | 237 | delta = create_delta(self._index, | 238 | c_max_delta_size = max_delta_size |
2121 | 238 | target, target_size, | 239 | with nogil: |
2122 | 239 | &delta_size, max_delta_size) | 240 | delta = create_delta(self._index, |
2123 | 241 | target, target_size, | ||
2124 | 242 | &delta_size, c_max_delta_size) | ||
2125 | 240 | result = None | 243 | result = None |
2126 | 241 | if delta: | 244 | if delta: |
2127 | 242 | result = PyString_FromStringAndSize(<char *>delta, delta_size) | 245 | result = PyString_FromStringAndSize(<char *>delta, delta_size) |
2128 | @@ -276,7 +279,8 @@ | |||
2129 | 276 | 279 | ||
2130 | 277 | 280 | ||
2131 | 278 | cdef unsigned char *_decode_copy_instruction(unsigned char *bytes, | 281 | cdef unsigned char *_decode_copy_instruction(unsigned char *bytes, |
2133 | 279 | unsigned char cmd, unsigned int *offset, unsigned int *length): # cannot_raise | 282 | unsigned char cmd, unsigned int *offset, |
2134 | 283 | unsigned int *length) nogil: # cannot_raise | ||
2135 | 280 | """Decode a copy instruction from the next few bytes. | 284 | """Decode a copy instruction from the next few bytes. |
2136 | 281 | 285 | ||
2137 | 282 | A copy instruction is a variable number of bytes, so we will parse the | 286 | A copy instruction is a variable number of bytes, so we will parse the |
2138 | @@ -326,6 +330,7 @@ | |||
2139 | 326 | cdef unsigned char *dst_buf, *out, cmd | 330 | cdef unsigned char *dst_buf, *out, cmd |
2140 | 327 | cdef Py_ssize_t size | 331 | cdef Py_ssize_t size |
2141 | 328 | cdef unsigned int cp_off, cp_size | 332 | cdef unsigned int cp_off, cp_size |
2142 | 333 | cdef int failed | ||
2143 | 329 | 334 | ||
2144 | 330 | data = <unsigned char *>delta | 335 | data = <unsigned char *>delta |
2145 | 331 | top = data + delta_size | 336 | top = data + delta_size |
2146 | @@ -335,37 +340,49 @@ | |||
2147 | 335 | result = PyString_FromStringAndSize(NULL, size) | 340 | result = PyString_FromStringAndSize(NULL, size) |
2148 | 336 | dst_buf = <unsigned char*>PyString_AS_STRING(result) | 341 | dst_buf = <unsigned char*>PyString_AS_STRING(result) |
2149 | 337 | 342 | ||
2181 | 338 | out = dst_buf | 343 | failed = 0 |
2182 | 339 | while (data < top): | 344 | with nogil: |
2183 | 340 | cmd = data[0] | 345 | out = dst_buf |
2184 | 341 | data = data + 1 | 346 | while (data < top): |
2185 | 342 | if (cmd & 0x80): | 347 | cmd = data[0] |
2186 | 343 | # Copy instruction | 348 | data = data + 1 |
2187 | 344 | data = _decode_copy_instruction(data, cmd, &cp_off, &cp_size) | 349 | if (cmd & 0x80): |
2188 | 345 | if (cp_off + cp_size < cp_size or | 350 | # Copy instruction |
2189 | 346 | cp_off + cp_size > source_size or | 351 | data = _decode_copy_instruction(data, cmd, &cp_off, &cp_size) |
2190 | 347 | cp_size > size): | 352 | if (cp_off + cp_size < cp_size or |
2191 | 348 | raise RuntimeError('Something wrong with:' | 353 | cp_off + cp_size > source_size or |
2192 | 349 | ' cp_off = %s, cp_size = %s' | 354 | cp_size > size): |
2193 | 350 | ' source_size = %s, size = %s' | 355 | failed = 1 |
2194 | 351 | % (cp_off, cp_size, source_size, size)) | 356 | break |
2195 | 352 | memcpy(out, source + cp_off, cp_size) | 357 | memcpy(out, source + cp_off, cp_size) |
2196 | 353 | out = out + cp_size | 358 | out = out + cp_size |
2197 | 354 | size = size - cp_size | 359 | size = size - cp_size |
2198 | 355 | else: | 360 | else: |
2199 | 356 | # Insert instruction | 361 | # Insert instruction |
2200 | 357 | if cmd == 0: | 362 | if cmd == 0: |
2201 | 358 | # cmd == 0 is reserved for future encoding | 363 | # cmd == 0 is reserved for future encoding |
2202 | 359 | # extensions. In the mean time we must fail when | 364 | # extensions. In the mean time we must fail when |
2203 | 360 | # encountering them (might be data corruption). | 365 | # encountering them (might be data corruption). |
2204 | 361 | raise RuntimeError('Got delta opcode: 0, not supported') | 366 | failed = 2 |
2205 | 362 | if (cmd > size): | 367 | break |
2206 | 363 | raise RuntimeError('Insert instruction longer than remaining' | 368 | if cmd > size: |
2207 | 364 | ' bytes: %d > %d' % (cmd, size)) | 369 | failed = 3 |
2208 | 365 | memcpy(out, data, cmd) | 370 | break |
2209 | 366 | out = out + cmd | 371 | memcpy(out, data, cmd) |
2210 | 367 | data = data + cmd | 372 | out = out + cmd |
2211 | 368 | size = size - cmd | 373 | data = data + cmd |
2212 | 374 | size = size - cmd | ||
2213 | 375 | if failed: | ||
2214 | 376 | if failed == 1: | ||
2215 | 377 | raise ValueError('Something wrong with:' | ||
2216 | 378 | ' cp_off = %s, cp_size = %s' | ||
2217 | 379 | ' source_size = %s, size = %s' | ||
2218 | 380 | % (cp_off, cp_size, source_size, size)) | ||
2219 | 381 | elif failed == 2: | ||
2220 | 382 | raise ValueError('Got delta opcode: 0, not supported') | ||
2221 | 383 | elif failed == 3: | ||
2222 | 384 | raise ValueError('Insert instruction longer than remaining' | ||
2223 | 385 | ' bytes: %d > %d' % (cmd, size)) | ||
2224 | 369 | 386 | ||
2225 | 370 | # sanity check | 387 | # sanity check |
2226 | 371 | if (data != top or size != 0): | 388 | if (data != top or size != 0): |
2227 | 372 | 389 | ||
2228 | === added file 'bzrlib/_import_c_api.h' | |||
2229 | --- bzrlib/_import_c_api.h 1970-01-01 00:00:00 +0000 | |||
2230 | +++ bzrlib/_import_c_api.h 2010-02-18 00:00:51 +0000 | |||
2231 | @@ -0,0 +1,189 @@ | |||
2232 | 1 | /* Copyright (C) 2009 Canonical Ltd | ||
2233 | 2 | * | ||
2234 | 3 | * This program is free software; you can redistribute it and/or modify | ||
2235 | 4 | * it under the terms of the GNU General Public License as published by | ||
2236 | 5 | * the Free Software Foundation; either version 2 of the License, or | ||
2237 | 6 | * (at your option) any later version. | ||
2238 | 7 | * | ||
2239 | 8 | * This program is distributed in the hope that it will be useful, | ||
2240 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2241 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2242 | 11 | * GNU General Public License for more details. | ||
2243 | 12 | * | ||
2244 | 13 | * You should have received a copy of the GNU General Public License | ||
2245 | 14 | * along with this program; if not, write to the Free Software | ||
2246 | 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
2247 | 16 | */ | ||
2248 | 17 | |||
2249 | 18 | #ifndef _IMPORT_C_API_H_ | ||
2250 | 19 | #define _IMPORT_C_API_H_ | ||
2251 | 20 | |||
2252 | 21 | /** | ||
2253 | 22 | * Helper functions to eliminate some of the boilerplate when importing a C API | ||
2254 | 23 | * from a CPython extension module. | ||
2255 | 24 | * | ||
2256 | 25 | * For more information see _export_c_api.h | ||
2257 | 26 | */ | ||
2258 | 27 | |||
2259 | 28 | static const char *_C_API_NAME = "_C_API"; | ||
2260 | 29 | |||
2261 | 30 | /** | ||
2262 | 31 | * Import a function from the _C_API_NAME dict that is part of module. | ||
2263 | 32 | * | ||
2264 | 33 | * @param module The Python module we are importing from | ||
2265 | 34 | * the attribute _C_API_NAME will be used as a dictionary | ||
2266 | 35 | * containing the function pointer we are looking for. | ||
2267 | 36 | * @param funcname Name of the function we want to import | ||
2268 | 37 | * @param func A pointer to the function handle where we will store the | ||
2269 | 38 | * function. | ||
2270 | 39 | * @param signature The C signature of the function. This is validated | ||
2271 | 40 | * against the signature stored in the C api, to make sure | ||
2272 | 41 | * there is no versioning skew. | ||
2273 | 42 | */ | ||
2274 | 43 | static int _import_function(PyObject *module, const char *funcname, | ||
2275 | 44 | void **func, const char *signature) | ||
2276 | 45 | { | ||
2277 | 46 | PyObject *d = NULL; | ||
2278 | 47 | PyObject *c_obj = NULL; | ||
2279 | 48 | const char *desc = NULL; | ||
2280 | 49 | |||
2281 | 50 | /* (char *) because Python2.4 defines this as (char *) rather than | ||
2282 | 51 | * (const char *) | ||
2283 | 52 | */ | ||
2284 | 53 | d = PyObject_GetAttrString(module, (char *)_C_API_NAME); | ||
2285 | 54 | if (!d) { | ||
2286 | 55 | // PyObject_GetAttrString sets an appropriate exception | ||
2287 | 56 | goto bad; | ||
2288 | 57 | } | ||
2289 | 58 | c_obj = PyDict_GetItemString(d, funcname); | ||
2290 | 59 | if (!c_obj) { | ||
2291 | 60 | // PyDict_GetItemString does not set an exception | ||
2292 | 61 | PyErr_Format(PyExc_AttributeError, | ||
2293 | 62 | "Module %s did not export a function named %s\n", | ||
2294 | 63 | PyModule_GetName(module), funcname); | ||
2295 | 64 | goto bad; | ||
2296 | 65 | } | ||
2297 | 66 | desc = (char *)PyCObject_GetDesc(c_obj); | ||
2298 | 67 | if (!desc || strcmp(desc, signature) != 0) { | ||
2299 | 68 | if (desc == NULL) { | ||
2300 | 69 | desc = "<null>"; | ||
2301 | 70 | } | ||
2302 | 71 | PyErr_Format(PyExc_TypeError, | ||
2303 | 72 | "C function %s.%s has wrong signature (expected %s, got %s)", | ||
2304 | 73 | PyModule_GetName(module), funcname, signature, desc); | ||
2305 | 74 | goto bad; | ||
2306 | 75 | } | ||
2307 | 76 | *func = PyCObject_AsVoidPtr(c_obj); | ||
2308 | 77 | Py_DECREF(d); | ||
2309 | 78 | return 0; | ||
2310 | 79 | bad: | ||
2311 | 80 | Py_XDECREF(d); | ||
2312 | 81 | return -1; | ||
2313 | 82 | } | ||
2314 | 83 | |||
2315 | 84 | |||
2316 | 85 | /** | ||
2317 | 86 | * Get a pointer to an exported PyTypeObject. | ||
2318 | 87 | * | ||
2319 | 88 | * @param module The Python module we are importing from | ||
2320 | 89 | * @param class_name Attribute of the module that should reference the | ||
2321 | 90 | * Type object. Note that a PyTypeObject is the python | ||
2322 | 91 | * description of the type, not the raw C structure. | ||
2323 | 92 | * @return A Pointer to the requested type object. On error NULL will be | ||
2324 | 93 | * returned and an exception will be set. | ||
2325 | 94 | */ | ||
2326 | 95 | static PyTypeObject * | ||
2327 | 96 | _import_type(PyObject *module, const char *class_name) | ||
2328 | 97 | { | ||
2329 | 98 | PyObject *type = NULL; | ||
2330 | 99 | |||
2331 | 100 | type = PyObject_GetAttrString(module, (char *)class_name); | ||
2332 | 101 | if (!type) { | ||
2333 | 102 | goto bad; | ||
2334 | 103 | } | ||
2335 | 104 | if (!PyType_Check(type)) { | ||
2336 | 105 | PyErr_Format(PyExc_TypeError, | ||
2337 | 106 | "%s.%s is not a type object", | ||
2338 | 107 | PyModule_GetName(module), class_name); | ||
2339 | 108 | goto bad; | ||
2340 | 109 | } | ||
2341 | 110 | return (PyTypeObject *)type; | ||
2342 | 111 | bad: | ||
2343 | 112 | Py_XDECREF(type); | ||
2344 | 113 | return NULL; | ||
2345 | 114 | } | ||
2346 | 115 | |||
2347 | 116 | |||
2348 | 117 | struct function_description | ||
2349 | 118 | { | ||
2350 | 119 | const char *name; | ||
2351 | 120 | void **pointer; | ||
2352 | 121 | const char *signature; | ||
2353 | 122 | }; | ||
2354 | 123 | |||
2355 | 124 | struct type_description | ||
2356 | 125 | { | ||
2357 | 126 | const char *name; | ||
2358 | 127 | PyTypeObject **pointer; | ||
2359 | 128 | }; | ||
2360 | 129 | |||
2361 | 130 | /** | ||
2362 | 131 | * Helper for importing several functions and types in a data-driven manner. | ||
2363 | 132 | * | ||
2364 | 133 | * @param module The name of the module we will be importing | ||
2365 | 134 | * @param functions A list of function_description objects, describing the | ||
2366 | 135 | * functions being imported. | ||
2367 | 136 | * The list should be terminated with {NULL} to indicate | ||
2368 | 137 | * there are no more functions to import. | ||
2369 | 138 | * @param types A list of type_description objects describing type | ||
2370 | 139 | * objects that we want to import. The list should be | ||
2371 | 140 | * terminated with {NULL} to indicate there are no more | ||
2372 | 141 | * types to import. | ||
2373 | 142 | * @return 0 on success, -1 on error and an exception should be set. | ||
2374 | 143 | */ | ||
2375 | 144 | |||
2376 | 145 | static int | ||
2377 | 146 | _import_extension_module(const char *module_name, | ||
2378 | 147 | struct function_description *functions, | ||
2379 | 148 | struct type_description *types) | ||
2380 | 149 | { | ||
2381 | 150 | PyObject *module = NULL; | ||
2382 | 151 | struct function_description *cur_func; | ||
2383 | 152 | struct type_description *cur_type; | ||
2384 | 153 | int ret_code; | ||
2385 | 154 | |||
2386 | 155 | module = PyImport_ImportModule((char *)module_name); | ||
2387 | 156 | if (!module) | ||
2388 | 157 | goto bad; | ||
2389 | 158 | if (functions != NULL) { | ||
2390 | 159 | cur_func = functions; | ||
2391 | 160 | while (cur_func->name != NULL) { | ||
2392 | 161 | ret_code = _import_function(module, cur_func->name, | ||
2393 | 162 | cur_func->pointer, | ||
2394 | 163 | cur_func->signature); | ||
2395 | 164 | if (ret_code < 0) | ||
2396 | 165 | goto bad; | ||
2397 | 166 | cur_func++; | ||
2398 | 167 | } | ||
2399 | 168 | } | ||
2400 | 169 | if (types != NULL) { | ||
2401 | 170 | PyTypeObject *type_p = NULL; | ||
2402 | 171 | cur_type = types; | ||
2403 | 172 | while (cur_type->name != NULL) { | ||
2404 | 173 | type_p = _import_type(module, cur_type->name); | ||
2405 | 174 | if (type_p == NULL) | ||
2406 | 175 | goto bad; | ||
2407 | 176 | *(cur_type->pointer) = type_p; | ||
2408 | 177 | cur_type++; | ||
2409 | 178 | } | ||
2410 | 179 | } | ||
2411 | 180 | |||
2412 | 181 | Py_XDECREF(module); | ||
2413 | 182 | return 0; | ||
2414 | 183 | bad: | ||
2415 | 184 | Py_XDECREF(module); | ||
2416 | 185 | return -1; | ||
2417 | 186 | } | ||
2418 | 187 | |||
2419 | 188 | |||
2420 | 189 | #endif // _IMPORT_C_API_H_ | ||
2421 | 0 | 190 | ||
2422 | === modified file 'bzrlib/_knit_load_data_pyx.pyx' | |||
2423 | --- bzrlib/_knit_load_data_pyx.pyx 2010-01-04 23:13:20 +0000 | |||
2424 | +++ bzrlib/_knit_load_data_pyx.pyx 2010-02-18 00:00:51 +0000 | |||
2425 | @@ -1,4 +1,4 @@ | |||
2427 | 1 | # Copyright (C) 2007 Canonical Ltd | 1 | # Copyright (C) 2007-2010 Canonical Ltd |
2428 | 2 | # | 2 | # |
2429 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
2430 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
2431 | 5 | 5 | ||
2432 | === modified file 'bzrlib/_known_graph_py.py' | |||
2433 | --- bzrlib/_known_graph_py.py 2009-08-25 18:45:40 +0000 | |||
2434 | +++ bzrlib/_known_graph_py.py 2010-02-18 00:00:52 +0000 | |||
2435 | @@ -1,4 +1,4 @@ | |||
2437 | 1 | # Copyright (C) 2009 Canonical Ltd | 1 | # Copyright (C) 2009, 2010 Canonical Ltd |
2438 | 2 | # | 2 | # |
2439 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
2440 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
2441 | @@ -17,6 +17,7 @@ | |||
2442 | 17 | """Implementation of Graph algorithms when we have already loaded everything. | 17 | """Implementation of Graph algorithms when we have already loaded everything. |
2443 | 18 | """ | 18 | """ |
2444 | 19 | 19 | ||
2445 | 20 | from collections import deque | ||
2446 | 20 | from bzrlib import ( | 21 | from bzrlib import ( |
2447 | 21 | errors, | 22 | errors, |
2448 | 22 | revision, | 23 | revision, |
2449 | @@ -62,7 +63,7 @@ | |||
2450 | 62 | :param parent_map: A dictionary mapping key => parent_keys | 63 | :param parent_map: A dictionary mapping key => parent_keys |
2451 | 63 | """ | 64 | """ |
2452 | 64 | self._nodes = {} | 65 | self._nodes = {} |
2454 | 65 | # Maps {sorted(revision_id, revision_id): heads} | 66 | # Maps {frozenset(revision_id, revision_id): heads} |
2455 | 66 | self._known_heads = {} | 67 | self._known_heads = {} |
2456 | 67 | self.do_cache = do_cache | 68 | self.do_cache = do_cache |
2457 | 68 | self._initialize_nodes(parent_map) | 69 | self._initialize_nodes(parent_map) |
2458 | @@ -132,6 +133,71 @@ | |||
2459 | 132 | # Update known_parent_gdfos for a key we couldn't process | 133 | # Update known_parent_gdfos for a key we couldn't process |
2460 | 133 | known_parent_gdfos[child_key] = known_gdfo | 134 | known_parent_gdfos[child_key] = known_gdfo |
2461 | 134 | 135 | ||
2462 | 136 | def add_node(self, key, parent_keys): | ||
2463 | 137 | """Add a new node to the graph. | ||
2464 | 138 | |||
2465 | 139 | If this fills in a ghost, then the gdfos of all children will be | ||
2466 | 140 | updated accordingly. | ||
2467 | 141 | |||
2468 | 142 | :param key: The node being added. If this is a duplicate, this is a | ||
2469 | 143 | no-op. | ||
2470 | 144 | :param parent_keys: The parents of the given node. | ||
2471 | 145 | :return: None (should we return if this was a ghost, etc?) | ||
2472 | 146 | """ | ||
2473 | 147 | nodes = self._nodes | ||
2474 | 148 | if key in nodes: | ||
2475 | 149 | node = nodes[key] | ||
2476 | 150 | if node.parent_keys is None: | ||
2477 | 151 | node.parent_keys = parent_keys | ||
2478 | 152 | # A ghost is being added, we can no-longer trust the heads | ||
2479 | 153 | # cache, so clear it | ||
2480 | 154 | self._known_heads.clear() | ||
2481 | 155 | else: | ||
2482 | 156 | # Make sure we compare a list to a list, as tuple != list. | ||
2483 | 157 | parent_keys = list(parent_keys) | ||
2484 | 158 | existing_parent_keys = list(node.parent_keys) | ||
2485 | 159 | if parent_keys == existing_parent_keys: | ||
2486 | 160 | return # Identical content | ||
2487 | 161 | else: | ||
2488 | 162 | raise ValueError('Parent key mismatch, existing node %s' | ||
2489 | 163 | ' has parents of %s not %s' | ||
2490 | 164 | % (key, existing_parent_keys, parent_keys)) | ||
2491 | 165 | else: | ||
2492 | 166 | node = _KnownGraphNode(key, parent_keys) | ||
2493 | 167 | nodes[key] = node | ||
2494 | 168 | parent_gdfo = 0 | ||
2495 | 169 | for parent_key in parent_keys: | ||
2496 | 170 | try: | ||
2497 | 171 | parent_node = nodes[parent_key] | ||
2498 | 172 | except KeyError: | ||
2499 | 173 | parent_node = _KnownGraphNode(parent_key, None) | ||
2500 | 174 | # Ghosts and roots have gdfo 1 | ||
2501 | 175 | parent_node.gdfo = 1 | ||
2502 | 176 | nodes[parent_key] = parent_node | ||
2503 | 177 | if parent_gdfo < parent_node.gdfo: | ||
2504 | 178 | parent_gdfo = parent_node.gdfo | ||
2505 | 179 | parent_node.child_keys.append(key) | ||
2506 | 180 | node.gdfo = parent_gdfo + 1 | ||
2507 | 181 | # Now fill the gdfo to all children | ||
2508 | 182 | # Note that this loop is slightly inefficient, in that we may visit the | ||
2509 | 183 | # same child (and its decendents) more than once, however, it is | ||
2510 | 184 | # 'efficient' in that we only walk to nodes that would be updated, | ||
2511 | 185 | # rather than all nodes | ||
2512 | 186 | # We use a deque rather than a simple list stack, to go for BFD rather | ||
2513 | 187 | # than DFD. So that if a longer path is possible, we walk it before we | ||
2514 | 188 | # get to the final child | ||
2515 | 189 | pending = deque([node]) | ||
2516 | 190 | while pending: | ||
2517 | 191 | node = pending.popleft() | ||
2518 | 192 | next_gdfo = node.gdfo + 1 | ||
2519 | 193 | for child_key in node.child_keys: | ||
2520 | 194 | child = nodes[child_key] | ||
2521 | 195 | if child.gdfo < next_gdfo: | ||
2522 | 196 | # This child is being updated, we need to check its | ||
2523 | 197 | # children | ||
2524 | 198 | child.gdfo = next_gdfo | ||
2525 | 199 | pending.append(child) | ||
2526 | 200 | |||
2527 | 135 | def heads(self, keys): | 201 | def heads(self, keys): |
2528 | 136 | """Return the heads from amongst keys. | 202 | """Return the heads from amongst keys. |
2529 | 137 | 203 | ||
2530 | @@ -281,3 +347,26 @@ | |||
2531 | 281 | in tsort.merge_sort(as_parent_map, tip_key, | 347 | in tsort.merge_sort(as_parent_map, tip_key, |
2532 | 282 | mainline_revisions=None, | 348 | mainline_revisions=None, |
2533 | 283 | generate_revno=True)] | 349 | generate_revno=True)] |
2534 | 350 | |||
2535 | 351 | def get_parent_keys(self, key): | ||
2536 | 352 | """Get the parents for a key | ||
2537 | 353 | |||
2538 | 354 | Returns a list containg the parents keys. If the key is a ghost, | ||
2539 | 355 | None is returned. A KeyError will be raised if the key is not in | ||
2540 | 356 | the graph. | ||
2541 | 357 | |||
2542 | 358 | :param keys: Key to check (eg revision_id) | ||
2543 | 359 | :return: A list of parents | ||
2544 | 360 | """ | ||
2545 | 361 | return self._nodes[key].parent_keys | ||
2546 | 362 | |||
2547 | 363 | def get_child_keys(self, key): | ||
2548 | 364 | """Get the children for a key | ||
2549 | 365 | |||
2550 | 366 | Returns a list containg the children keys. A KeyError will be raised | ||
2551 | 367 | if the key is not in the graph. | ||
2552 | 368 | |||
2553 | 369 | :param keys: Key to check (eg revision_id) | ||
2554 | 370 | :return: A list of children | ||
2555 | 371 | """ | ||
2556 | 372 | return self._nodes[key].child_keys | ||
2557 | 284 | 373 | ||
2558 | === modified file 'bzrlib/_known_graph_pyx.pyx' | |||
2559 | --- bzrlib/_known_graph_pyx.pyx 2010-01-05 05:03:51 +0000 | |||
2560 | +++ bzrlib/_known_graph_pyx.pyx 2010-02-18 00:00:51 +0000 | |||
2561 | @@ -1,4 +1,4 @@ | |||
2563 | 1 | # Copyright (C) 2009 Canonical Ltd | 1 | # Copyright (C) 2009, 2010 Canonical Ltd |
2564 | 2 | # | 2 | # |
2565 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
2566 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
2567 | @@ -51,6 +51,7 @@ | |||
2568 | 51 | 51 | ||
2569 | 52 | void Py_INCREF(object) | 52 | void Py_INCREF(object) |
2570 | 53 | 53 | ||
2571 | 54 | from collections import deque | ||
2572 | 54 | import gc | 55 | import gc |
2573 | 55 | 56 | ||
2574 | 56 | from bzrlib import errors, revision | 57 | from bzrlib import errors, revision |
2575 | @@ -88,6 +89,18 @@ | |||
2576 | 88 | PyList_Append(keys, child.key) | 89 | PyList_Append(keys, child.key) |
2577 | 89 | return keys | 90 | return keys |
2578 | 90 | 91 | ||
2579 | 92 | property parent_keys: | ||
2580 | 93 | def __get__(self): | ||
2581 | 94 | if self.parents is None: | ||
2582 | 95 | return None | ||
2583 | 96 | |||
2584 | 97 | cdef _KnownGraphNode parent | ||
2585 | 98 | |||
2586 | 99 | keys = [] | ||
2587 | 100 | for parent in self.parents: | ||
2588 | 101 | PyList_Append(keys, parent.key) | ||
2589 | 102 | return keys | ||
2590 | 103 | |||
2591 | 91 | cdef clear_references(self): | 104 | cdef clear_references(self): |
2592 | 92 | self.parents = None | 105 | self.parents = None |
2593 | 93 | self.children = None | 106 | self.children = None |
2594 | @@ -180,7 +193,7 @@ | |||
2595 | 180 | """This is a class which assumes we already know the full graph.""" | 193 | """This is a class which assumes we already know the full graph.""" |
2596 | 181 | 194 | ||
2597 | 182 | cdef public object _nodes | 195 | cdef public object _nodes |
2599 | 183 | cdef object _known_heads | 196 | cdef public object _known_heads |
2600 | 184 | cdef public int do_cache | 197 | cdef public int do_cache |
2601 | 185 | 198 | ||
2602 | 186 | def __init__(self, parent_map, do_cache=True): | 199 | def __init__(self, parent_map, do_cache=True): |
2603 | @@ -220,6 +233,28 @@ | |||
2604 | 220 | node = <_KnownGraphNode>temp_node | 233 | node = <_KnownGraphNode>temp_node |
2605 | 221 | return node | 234 | return node |
2606 | 222 | 235 | ||
2607 | 236 | cdef _populate_parents(self, _KnownGraphNode node, parent_keys): | ||
2608 | 237 | cdef Py_ssize_t num_parent_keys, pos | ||
2609 | 238 | cdef _KnownGraphNode parent_node | ||
2610 | 239 | |||
2611 | 240 | num_parent_keys = len(parent_keys) | ||
2612 | 241 | # We know how many parents, so we pre allocate the tuple | ||
2613 | 242 | parent_nodes = PyTuple_New(num_parent_keys) | ||
2614 | 243 | for pos from 0 <= pos < num_parent_keys: | ||
2615 | 244 | # Note: it costs us 10ms out of 40ms to lookup all of these | ||
2616 | 245 | # parents, it doesn't seem to be an allocation overhead, | ||
2617 | 246 | # but rather a lookup overhead. There doesn't seem to be | ||
2618 | 247 | # a way around it, and that is one reason why | ||
2619 | 248 | # KnownGraphNode maintains a direct pointer to the parent | ||
2620 | 249 | # node. | ||
2621 | 250 | # We use [] because parent_keys may be a tuple or list | ||
2622 | 251 | parent_node = self._get_or_create_node(parent_keys[pos]) | ||
2623 | 252 | # PyTuple_SET_ITEM will steal a reference, so INCREF first | ||
2624 | 253 | Py_INCREF(parent_node) | ||
2625 | 254 | PyTuple_SET_ITEM(parent_nodes, pos, parent_node) | ||
2626 | 255 | PyList_Append(parent_node.children, node) | ||
2627 | 256 | node.parents = parent_nodes | ||
2628 | 257 | |||
2629 | 223 | def _initialize_nodes(self, parent_map): | 258 | def _initialize_nodes(self, parent_map): |
2630 | 224 | """Populate self._nodes. | 259 | """Populate self._nodes. |
2631 | 225 | 260 | ||
2632 | @@ -230,7 +265,7 @@ | |||
2633 | 230 | child keys, | 265 | child keys, |
2634 | 231 | """ | 266 | """ |
2635 | 232 | cdef PyObject *temp_key, *temp_parent_keys, *temp_node | 267 | cdef PyObject *temp_key, *temp_parent_keys, *temp_node |
2637 | 233 | cdef Py_ssize_t pos, pos2, num_parent_keys | 268 | cdef Py_ssize_t pos |
2638 | 234 | cdef _KnownGraphNode node | 269 | cdef _KnownGraphNode node |
2639 | 235 | cdef _KnownGraphNode parent_node | 270 | cdef _KnownGraphNode parent_node |
2640 | 236 | 271 | ||
2641 | @@ -241,24 +276,8 @@ | |||
2642 | 241 | while PyDict_Next(parent_map, &pos, &temp_key, &temp_parent_keys): | 276 | while PyDict_Next(parent_map, &pos, &temp_key, &temp_parent_keys): |
2643 | 242 | key = <object>temp_key | 277 | key = <object>temp_key |
2644 | 243 | parent_keys = <object>temp_parent_keys | 278 | parent_keys = <object>temp_parent_keys |
2645 | 244 | num_parent_keys = len(parent_keys) | ||
2646 | 245 | node = self._get_or_create_node(key) | 279 | node = self._get_or_create_node(key) |
2663 | 246 | # We know how many parents, so we pre allocate the tuple | 280 | self._populate_parents(node, parent_keys) |
2648 | 247 | parent_nodes = PyTuple_New(num_parent_keys) | ||
2649 | 248 | for pos2 from 0 <= pos2 < num_parent_keys: | ||
2650 | 249 | # Note: it costs us 10ms out of 40ms to lookup all of these | ||
2651 | 250 | # parents, it doesn't seem to be an allocation overhead, | ||
2652 | 251 | # but rather a lookup overhead. There doesn't seem to be | ||
2653 | 252 | # a way around it, and that is one reason why | ||
2654 | 253 | # KnownGraphNode maintains a direct pointer to the parent | ||
2655 | 254 | # node. | ||
2656 | 255 | # We use [] because parent_keys may be a tuple or list | ||
2657 | 256 | parent_node = self._get_or_create_node(parent_keys[pos2]) | ||
2658 | 257 | # PyTuple_SET_ITEM will steal a reference, so INCREF first | ||
2659 | 258 | Py_INCREF(parent_node) | ||
2660 | 259 | PyTuple_SET_ITEM(parent_nodes, pos2, parent_node) | ||
2661 | 260 | PyList_Append(parent_node.children, node) | ||
2662 | 261 | node.parents = parent_nodes | ||
2664 | 262 | 281 | ||
2665 | 263 | def _find_tails(self): | 282 | def _find_tails(self): |
2666 | 264 | cdef PyObject *temp_node | 283 | cdef PyObject *temp_node |
2667 | @@ -322,6 +341,76 @@ | |||
2668 | 322 | # anymore | 341 | # anymore |
2669 | 323 | child.seen = 0 | 342 | child.seen = 0 |
2670 | 324 | 343 | ||
2671 | 344 | def add_node(self, key, parent_keys): | ||
2672 | 345 | """Add a new node to the graph. | ||
2673 | 346 | |||
2674 | 347 | If this fills in a ghost, then the gdfos of all children will be | ||
2675 | 348 | updated accordingly. | ||
2676 | 349 | |||
2677 | 350 | :param key: The node being added. If this is a duplicate, this is a | ||
2678 | 351 | no-op. | ||
2679 | 352 | :param parent_keys: The parents of the given node. | ||
2680 | 353 | :return: None (should we return if this was a ghost, etc?) | ||
2681 | 354 | """ | ||
2682 | 355 | cdef PyObject *maybe_node | ||
2683 | 356 | cdef _KnownGraphNode node, parent_node, child_node | ||
2684 | 357 | cdef long parent_gdfo, next_gdfo | ||
2685 | 358 | |||
2686 | 359 | maybe_node = PyDict_GetItem(self._nodes, key) | ||
2687 | 360 | if maybe_node != NULL: | ||
2688 | 361 | node = <_KnownGraphNode>maybe_node | ||
2689 | 362 | if node.parents is None: | ||
2690 | 363 | # We are filling in a ghost | ||
2691 | 364 | self._populate_parents(node, parent_keys) | ||
2692 | 365 | # We can't trust cached heads anymore | ||
2693 | 366 | self._known_heads.clear() | ||
2694 | 367 | else: # Ensure that the parent_key list matches | ||
2695 | 368 | existing_parent_keys = [] | ||
2696 | 369 | for parent_node in node.parents: | ||
2697 | 370 | existing_parent_keys.append(parent_node.key) | ||
2698 | 371 | # Make sure we use a list for the comparison, in case it was a | ||
2699 | 372 | # tuple, etc | ||
2700 | 373 | parent_keys = list(parent_keys) | ||
2701 | 374 | if existing_parent_keys == parent_keys: | ||
2702 | 375 | # Exact match, nothing more to do | ||
2703 | 376 | return | ||
2704 | 377 | else: | ||
2705 | 378 | raise ValueError('Parent key mismatch, existing node %s' | ||
2706 | 379 | ' has parents of %s not %s' | ||
2707 | 380 | % (key, existing_parent_keys, parent_keys)) | ||
2708 | 381 | else: | ||
2709 | 382 | node = _KnownGraphNode(key) | ||
2710 | 383 | PyDict_SetItem(self._nodes, key, node) | ||
2711 | 384 | self._populate_parents(node, parent_keys) | ||
2712 | 385 | parent_gdfo = 0 | ||
2713 | 386 | for parent_node in node.parents: | ||
2714 | 387 | if parent_node.gdfo == -1: | ||
2715 | 388 | # This is a newly introduced ghost, so it gets gdfo of 1 | ||
2716 | 389 | parent_node.gdfo = 1 | ||
2717 | 390 | if parent_gdfo < parent_node.gdfo: | ||
2718 | 391 | parent_gdfo = parent_node.gdfo | ||
2719 | 392 | node.gdfo = parent_gdfo + 1 | ||
2720 | 393 | # Now fill the gdfo to all children | ||
2721 | 394 | # Note that this loop is slightly inefficient, in that we may visit the | ||
2722 | 395 | # same child (and its decendents) more than once, however, it is | ||
2723 | 396 | # 'efficient' in that we only walk to nodes that would be updated, | ||
2724 | 397 | # rather than all nodes | ||
2725 | 398 | # We use a deque rather than a simple list stack, to go for BFD rather | ||
2726 | 399 | # than DFD. So that if a longer path is possible, we walk it before we | ||
2727 | 400 | # get to the final child | ||
2728 | 401 | pending = deque([node]) | ||
2729 | 402 | pending_popleft = pending.popleft | ||
2730 | 403 | pending_append = pending.append | ||
2731 | 404 | while pending: | ||
2732 | 405 | node = pending_popleft() | ||
2733 | 406 | next_gdfo = node.gdfo + 1 | ||
2734 | 407 | for child_node in node.children: | ||
2735 | 408 | if child_node.gdfo < next_gdfo: | ||
2736 | 409 | # This child is being updated, we need to check its | ||
2737 | 410 | # children | ||
2738 | 411 | child_node.gdfo = next_gdfo | ||
2739 | 412 | pending_append(child_node) | ||
2740 | 413 | |||
2741 | 325 | def heads(self, keys): | 414 | def heads(self, keys): |
2742 | 326 | """Return the heads from amongst keys. | 415 | """Return the heads from amongst keys. |
2743 | 327 | 416 | ||
2744 | @@ -549,6 +638,29 @@ | |||
2745 | 549 | # shown a specific impact, yet. | 638 | # shown a specific impact, yet. |
2746 | 550 | sorter = _MergeSorter(self, tip_key) | 639 | sorter = _MergeSorter(self, tip_key) |
2747 | 551 | return sorter.topo_order() | 640 | return sorter.topo_order() |
2748 | 641 | |||
2749 | 642 | def get_parent_keys(self, key): | ||
2750 | 643 | """Get the parents for a key | ||
2751 | 644 | |||
2752 | 645 | Returns a list containg the parents keys. If the key is a ghost, | ||
2753 | 646 | None is returned. A KeyError will be raised if the key is not in | ||
2754 | 647 | the graph. | ||
2755 | 648 | |||
2756 | 649 | :param keys: Key to check (eg revision_id) | ||
2757 | 650 | :return: A list of parents | ||
2758 | 651 | """ | ||
2759 | 652 | return self._nodes[key].parent_keys | ||
2760 | 653 | |||
2761 | 654 | def get_child_keys(self, key): | ||
2762 | 655 | """Get the children for a key | ||
2763 | 656 | |||
2764 | 657 | Returns a list containg the children keys. A KeyError will be raised | ||
2765 | 658 | if the key is not in the graph. | ||
2766 | 659 | |||
2767 | 660 | :param keys: Key to check (eg revision_id) | ||
2768 | 661 | :return: A list of children | ||
2769 | 662 | """ | ||
2770 | 663 | return self._nodes[key].child_keys | ||
2771 | 552 | 664 | ||
2772 | 553 | 665 | ||
2773 | 554 | cdef class _MergeSortNode: | 666 | cdef class _MergeSortNode: |
2774 | 555 | 667 | ||
2775 | === modified file 'bzrlib/_patiencediff_c.c' | |||
2776 | --- bzrlib/_patiencediff_c.c 2009-03-23 14:59:43 +0000 | |||
2777 | +++ bzrlib/_patiencediff_c.c 2010-02-18 00:00:51 +0000 | |||
2778 | @@ -298,7 +298,7 @@ | |||
2779 | 298 | apos = SENTINEL; | 298 | apos = SENTINEL; |
2780 | 299 | /* loop through all lines in the linked list */ | 299 | /* loop through all lines in the linked list */ |
2781 | 300 | for (i = h[equiv].a_pos; i != SENTINEL; i = lines_a[i].next) { | 300 | for (i = h[equiv].a_pos; i != SENTINEL; i = lines_a[i].next) { |
2783 | 301 | /* the index is lower than alo, the the next line */ | 301 | /* the index is lower than alo, continue to the next line */ |
2784 | 302 | if (i < alo) { | 302 | if (i < alo) { |
2785 | 303 | h[equiv].a_pos = i; | 303 | h[equiv].a_pos = i; |
2786 | 304 | continue; | 304 | continue; |
2787 | @@ -319,7 +319,7 @@ | |||
2788 | 319 | /* check for duplicates of this line in lines_b[blo:bhi] */ | 319 | /* check for duplicates of this line in lines_b[blo:bhi] */ |
2789 | 320 | /* loop through all lines in the linked list */ | 320 | /* loop through all lines in the linked list */ |
2790 | 321 | for (i = h[equiv].b_pos; i != SENTINEL; i = lines_b[i].next) { | 321 | for (i = h[equiv].b_pos; i != SENTINEL; i = lines_b[i].next) { |
2792 | 322 | /* the index is lower than blo, the the next line */ | 322 | /* the index is lower than blo, continue to the next line */ |
2793 | 323 | if (i < blo) { | 323 | if (i < blo) { |
2794 | 324 | h[equiv].b_pos = i; | 324 | h[equiv].b_pos = i; |
2795 | 325 | continue; | 325 | continue; |
2796 | 326 | 326 | ||
2797 | === modified file 'bzrlib/_readdir_pyx.pyx' | |||
2798 | --- bzrlib/_readdir_pyx.pyx 2009-12-23 02:19:04 +0000 | |||
2799 | +++ bzrlib/_readdir_pyx.pyx 2010-02-18 00:00:50 +0000 | |||
2800 | @@ -1,4 +1,4 @@ | |||
2802 | 1 | # Copyright (C) 2006, 2008, 2009 Canonical Ltd | 1 | # Copyright (C) 2006, 2008, 2009, 2010 Canonical Ltd |
2803 | 2 | # | 2 | # |
2804 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
2805 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
2806 | 5 | 5 | ||
2807 | === modified file 'bzrlib/_rio_pyx.pyx' | |||
2808 | --- bzrlib/_rio_pyx.pyx 2010-01-05 04:59:57 +0000 | |||
2809 | +++ bzrlib/_rio_pyx.pyx 2010-02-18 00:00:52 +0000 | |||
2810 | @@ -1,4 +1,4 @@ | |||
2812 | 1 | # Copyright (C) 2009 Canonical Ltd | 1 | # Copyright (C) 2009, 2010 Canonical Ltd |
2813 | 2 | # | 2 | # |
2814 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
2815 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
2816 | 5 | 5 | ||
2817 | === added file 'bzrlib/_simple_set_pyx.pxd' | |||
2818 | --- bzrlib/_simple_set_pyx.pxd 1970-01-01 00:00:00 +0000 | |||
2819 | +++ bzrlib/_simple_set_pyx.pxd 2010-02-18 00:00:50 +0000 | |||
2820 | @@ -0,0 +1,91 @@ | |||
2821 | 1 | # Copyright (C) 2009, 2010 Canonical Ltd | ||
2822 | 2 | # | ||
2823 | 3 | # This program is free software; you can redistribute it and/or modify | ||
2824 | 4 | # it under the terms of the GNU General Public License as published by | ||
2825 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
2826 | 6 | # (at your option) any later version. | ||
2827 | 7 | # | ||
2828 | 8 | # This program is distributed in the hope that it will be useful, | ||
2829 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2830 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2831 | 11 | # GNU General Public License for more details. | ||
2832 | 12 | # | ||
2833 | 13 | # You should have received a copy of the GNU General Public License | ||
2834 | 14 | # along with this program; if not, write to the Free Software | ||
2835 | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
2836 | 16 | |||
2837 | 17 | """Interface definition of a class like PySet but without caching the hash. | ||
2838 | 18 | |||
2839 | 19 | This is generally useful when you want to 'intern' objects, etc. Note that this | ||
2840 | 20 | differs from Set in that we: | ||
2841 | 21 | 1) Don't have all of the .intersection, .difference, etc functions | ||
2842 | 22 | 2) Do return the object from the set via queries | ||
2843 | 23 | eg. SimpleSet.add(key) => saved_key and SimpleSet[key] => saved_key | ||
2844 | 24 | """ | ||
2845 | 25 | |||
2846 | 26 | cdef extern from "Python.h": | ||
2847 | 27 | ctypedef struct PyObject: | ||
2848 | 28 | pass | ||
2849 | 29 | |||
2850 | 30 | |||
2851 | 31 | cdef public api class SimpleSet [object SimpleSetObject, type SimpleSet_Type]: | ||
2852 | 32 | """A class similar to PySet, but with simpler implementation. | ||
2853 | 33 | |||
2854 | 34 | The main advantage is that this class uses only 2N memory to store N | ||
2855 | 35 | objects rather than 4N memory. The main trade-off is that we do not cache | ||
2856 | 36 | the hash value of saved objects. As such, it is assumed that computing the | ||
2857 | 37 | hash will be cheap (such as strings or tuples of strings, etc.) | ||
2858 | 38 | |||
2859 | 39 | This also differs in that you can get back the objects that are stored | ||
2860 | 40 | (like a dict), but we also don't implement the complete list of 'set' | ||
2861 | 41 | operations (difference, intersection, etc). | ||
2862 | 42 | """ | ||
2863 | 43 | # Data structure definition: | ||
2864 | 44 | # This is a basic hash table using open addressing. | ||
2865 | 45 | # http://en.wikipedia.org/wiki/Open_addressing | ||
2866 | 46 | # Basically that means we keep an array of pointers to Python objects | ||
2867 | 47 | # (called a table). Each location in the array is called a 'slot'. | ||
2868 | 48 | # | ||
2869 | 49 | # An empty slot holds a NULL pointer, a slot where there was an item | ||
2870 | 50 | # which was then deleted will hold a pointer to _dummy, and a filled slot | ||
2871 | 51 | # points at the actual object which fills that slot. | ||
2872 | 52 | # | ||
2873 | 53 | # The table is always a power of two, and the default location where an | ||
2874 | 54 | # object is inserted is at hash(object) & (table_size - 1) | ||
2875 | 55 | # | ||
2876 | 56 | # If there is a collision, then we search for another location. The | ||
2877 | 57 | # specific algorithm is in _lookup. We search until we: | ||
2878 | 58 | # find the object | ||
2879 | 59 | # find an equivalent object (by tp_richcompare(obj1, obj2, Py_EQ)) | ||
2880 | 60 | # find a NULL slot | ||
2881 | 61 | # | ||
2882 | 62 | # When an object is deleted, we set its slot to _dummy. this way we don't | ||
2883 | 63 | # have to track whether there was a collision, and find the corresponding | ||
2884 | 64 | # keys. (The collision resolution algorithm makes that nearly impossible | ||
2885 | 65 | # anyway, because it depends on the upper bits of the hash.) | ||
2886 | 66 | # The main effect of this, is that if we find _dummy, then we can insert | ||
2887 | 67 | # an object there, but we have to keep searching until we find NULL to | ||
2888 | 68 | # know that the object is not present elsewhere. | ||
2889 | 69 | |||
2890 | 70 | cdef Py_ssize_t _used # active | ||
2891 | 71 | cdef Py_ssize_t _fill # active + dummy | ||
2892 | 72 | cdef Py_ssize_t _mask # Table contains (mask+1) slots, a power of 2 | ||
2893 | 73 | cdef PyObject **_table # Pyrex/Cython doesn't support arrays to 'object' | ||
2894 | 74 | # so we manage it manually | ||
2895 | 75 | |||
2896 | 76 | cdef PyObject *_get(self, object key) except? NULL | ||
2897 | 77 | cdef object _add(self, key) | ||
2898 | 78 | cdef int _discard(self, key) except -1 | ||
2899 | 79 | cdef int _insert_clean(self, PyObject *key) except -1 | ||
2900 | 80 | cdef Py_ssize_t _resize(self, Py_ssize_t min_unused) except -1 | ||
2901 | 81 | |||
2902 | 82 | |||
2903 | 83 | # TODO: might want to export the C api here, though it is all available from | ||
2904 | 84 | # the class object... | ||
2905 | 85 | cdef api SimpleSet SimpleSet_New() | ||
2906 | 86 | cdef api object SimpleSet_Add(object self, object key) | ||
2907 | 87 | cdef api int SimpleSet_Contains(object self, object key) except -1 | ||
2908 | 88 | cdef api int SimpleSet_Discard(object self, object key) except -1 | ||
2909 | 89 | cdef api PyObject *SimpleSet_Get(SimpleSet self, object key) except? NULL | ||
2910 | 90 | cdef api Py_ssize_t SimpleSet_Size(object self) except -1 | ||
2911 | 91 | cdef api int SimpleSet_Next(object self, Py_ssize_t *pos, PyObject **key) except -1 | ||
2912 | 0 | 92 | ||
2913 | === added file 'bzrlib/_simple_set_pyx.pyx' | |||
2914 | --- bzrlib/_simple_set_pyx.pyx 1970-01-01 00:00:00 +0000 | |||
2915 | +++ bzrlib/_simple_set_pyx.pyx 2010-02-18 00:00:50 +0000 | |||
2916 | @@ -0,0 +1,592 @@ | |||
2917 | 1 | # Copyright (C) 2009, 2010 Canonical Ltd | ||
2918 | 2 | # | ||
2919 | 3 | # This program is free software; you can redistribute it and/or modify | ||
2920 | 4 | # it under the terms of the GNU General Public License as published by | ||
2921 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
2922 | 6 | # (at your option) any later version. | ||
2923 | 7 | # | ||
2924 | 8 | # This program is distributed in the hope that it will be useful, | ||
2925 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2926 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2927 | 11 | # GNU General Public License for more details. | ||
2928 | 12 | # | ||
2929 | 13 | # You should have received a copy of the GNU General Public License | ||
2930 | 14 | # along with this program; if not, write to the Free Software | ||
2931 | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
2932 | 16 | |||
2933 | 17 | """Definition of a class that is similar to Set with some small changes.""" | ||
2934 | 18 | |||
2935 | 19 | cdef extern from "python-compat.h": | ||
2936 | 20 | pass | ||
2937 | 21 | |||
2938 | 22 | cdef extern from "Python.h": | ||
2939 | 23 | ctypedef unsigned long size_t | ||
2940 | 24 | ctypedef long (*hashfunc)(PyObject*) except -1 | ||
2941 | 25 | ctypedef object (*richcmpfunc)(PyObject *, PyObject *, int) | ||
2942 | 26 | ctypedef int (*visitproc)(PyObject *, void *) | ||
2943 | 27 | ctypedef int (*traverseproc)(PyObject *, visitproc, void *) | ||
2944 | 28 | int Py_EQ | ||
2945 | 29 | void Py_INCREF(PyObject *) | ||
2946 | 30 | void Py_DECREF(PyObject *) | ||
2947 | 31 | ctypedef struct PyTypeObject: | ||
2948 | 32 | hashfunc tp_hash | ||
2949 | 33 | richcmpfunc tp_richcompare | ||
2950 | 34 | traverseproc tp_traverse | ||
2951 | 35 | |||
2952 | 36 | PyTypeObject *Py_TYPE(PyObject *) | ||
2953 | 37 | # Note: *Don't* use hash(), Pyrex 0.9.8.5 thinks it returns an 'int', and | ||
2954 | 38 | # thus silently truncates to 32-bits on 64-bit machines. | ||
2955 | 39 | long PyObject_Hash(PyObject *) except -1 | ||
2956 | 40 | |||
2957 | 41 | void *PyMem_Malloc(size_t nbytes) | ||
2958 | 42 | void PyMem_Free(void *) | ||
2959 | 43 | void memset(void *, int, size_t) | ||
2960 | 44 | |||
2961 | 45 | |||
2962 | 46 | # Dummy is an object used to mark nodes that have been deleted. Since | ||
2963 | 47 | # collisions require us to move a node to an alternative location, if we just | ||
2964 | 48 | # set an entry to NULL on delete, we won't find any relocated nodes. | ||
2965 | 49 | # We have to use _dummy_obj because we need to keep a refcount to it, but we | ||
2966 | 50 | # also use _dummy as a pointer, because it avoids having to put <PyObject*> all | ||
2967 | 51 | # over the code base. | ||
2968 | 52 | cdef object _dummy_obj | ||
2969 | 53 | cdef PyObject *_dummy | ||
2970 | 54 | _dummy_obj = object() | ||
2971 | 55 | _dummy = <PyObject *>_dummy_obj | ||
2972 | 56 | |||
2973 | 57 | |||
2974 | 58 | cdef object _NotImplemented | ||
2975 | 59 | _NotImplemented = NotImplemented | ||
2976 | 60 | |||
2977 | 61 | |||
2978 | 62 | cdef int _is_equal(PyObject *this, long this_hash, PyObject *other) except -1: | ||
2979 | 63 | cdef long other_hash | ||
2980 | 64 | |||
2981 | 65 | if this == other: | ||
2982 | 66 | return 1 | ||
2983 | 67 | other_hash = PyObject_Hash(other) | ||
2984 | 68 | if other_hash != this_hash: | ||
2985 | 69 | return 0 | ||
2986 | 70 | |||
2987 | 71 | # This implements a subset of the PyObject_RichCompareBool functionality. | ||
2988 | 72 | # Namely it: | ||
2989 | 73 | # 1) Doesn't try to do anything with old-style classes | ||
2990 | 74 | # 2) Assumes that both objects have a tp_richcompare implementation, and | ||
2991 | 75 | # that if that is not enough to compare equal, then they are not | ||
2992 | 76 | # equal. (It doesn't try to cast them both to some intermediate form | ||
2993 | 77 | # that would compare equal.) | ||
2994 | 78 | res = Py_TYPE(this).tp_richcompare(this, other, Py_EQ) | ||
2995 | 79 | if res is _NotImplemented: | ||
2996 | 80 | res = Py_TYPE(other).tp_richcompare(other, this, Py_EQ) | ||
2997 | 81 | if res is _NotImplemented: | ||
2998 | 82 | return 0 | ||
2999 | 83 | if res: | ||
3000 | 84 | return 1 | ||
3001 | 85 | return 0 | ||
3002 | 86 | |||
3003 | 87 | |||
3004 | 88 | cdef public api class SimpleSet [object SimpleSetObject, type SimpleSet_Type]: | ||
3005 | 89 | """This class can be used to track canonical forms for objects. | ||
3006 | 90 | |||
3007 | 91 | It is similar in function to the interned dictionary that is used by | ||
3008 | 92 | strings. However: | ||
3009 | 93 | |||
3010 | 94 | 1) It assumes that hash(obj) is cheap, so does not need to inline a copy | ||
3011 | 95 | of it | ||
3012 | 96 | 2) It only stores one reference to the object, rather than 2 (key vs | ||
3013 | 97 | key:value) | ||
3014 | 98 | |||
3015 | 99 | As such, it uses 1/3rd the amount of memory to store a pointer to the | ||
3016 | 100 | interned object. | ||
3017 | 101 | """ | ||
3018 | 102 | # Attributes are defined in the .pxd file | ||
3019 | 103 | DEF DEFAULT_SIZE=1024 | ||
3020 | 104 | |||
3021 | 105 | def __init__(self): | ||
3022 | 106 | cdef Py_ssize_t size, n_bytes | ||
3023 | 107 | |||
3024 | 108 | size = DEFAULT_SIZE | ||
3025 | 109 | self._mask = size - 1 | ||
3026 | 110 | self._used = 0 | ||
3027 | 111 | self._fill = 0 | ||
3028 | 112 | n_bytes = sizeof(PyObject*) * size; | ||
3029 | 113 | self._table = <PyObject **>PyMem_Malloc(n_bytes) | ||
3030 | 114 | if self._table == NULL: | ||
3031 | 115 | raise MemoryError() | ||
3032 | 116 | memset(self._table, 0, n_bytes) | ||
3033 | 117 | |||
3034 | 118 | def __dealloc__(self): | ||
3035 | 119 | if self._table != NULL: | ||
3036 | 120 | PyMem_Free(self._table) | ||
3037 | 121 | self._table = NULL | ||
3038 | 122 | |||
3039 | 123 | property used: | ||
3040 | 124 | def __get__(self): | ||
3041 | 125 | return self._used | ||
3042 | 126 | |||
3043 | 127 | property fill: | ||
3044 | 128 | def __get__(self): | ||
3045 | 129 | return self._fill | ||
3046 | 130 | |||
3047 | 131 | property mask: | ||
3048 | 132 | def __get__(self): | ||
3049 | 133 | return self._mask | ||
3050 | 134 | |||
3051 | 135 | def _memory_size(self): | ||
3052 | 136 | """Return the number of bytes of memory consumed by this class.""" | ||
3053 | 137 | return sizeof(self) + (sizeof(PyObject*)*(self._mask + 1)) | ||
3054 | 138 | |||
3055 | 139 | def __len__(self): | ||
3056 | 140 | return self._used | ||
3057 | 141 | |||
3058 | 142 | def _test_lookup(self, key): | ||
3059 | 143 | cdef PyObject **slot | ||
3060 | 144 | |||
3061 | 145 | slot = _lookup(self, key) | ||
3062 | 146 | if slot[0] == NULL: | ||
3063 | 147 | res = '<null>' | ||
3064 | 148 | elif slot[0] == _dummy: | ||
3065 | 149 | res = '<dummy>' | ||
3066 | 150 | else: | ||
3067 | 151 | res = <object>slot[0] | ||
3068 | 152 | return <int>(slot - self._table), res | ||
3069 | 153 | |||
3070 | 154 | def __contains__(self, key): | ||
3071 | 155 | """Is key present in this SimpleSet.""" | ||
3072 | 156 | cdef PyObject **slot | ||
3073 | 157 | |||
3074 | 158 | slot = _lookup(self, key) | ||
3075 | 159 | if slot[0] == NULL or slot[0] == _dummy: | ||
3076 | 160 | return False | ||
3077 | 161 | return True | ||
3078 | 162 | |||
3079 | 163 | cdef PyObject *_get(self, object key) except? NULL: | ||
3080 | 164 | """Return the object (or nothing) define at the given location.""" | ||
3081 | 165 | cdef PyObject **slot | ||
3082 | 166 | |||
3083 | 167 | slot = _lookup(self, key) | ||
3084 | 168 | if slot[0] == NULL or slot[0] == _dummy: | ||
3085 | 169 | return NULL | ||
3086 | 170 | return slot[0] | ||
3087 | 171 | |||
3088 | 172 | def __getitem__(self, key): | ||
3089 | 173 | """Return a stored item that is equivalent to key.""" | ||
3090 | 174 | cdef PyObject *py_val | ||
3091 | 175 | |||
3092 | 176 | py_val = self._get(key) | ||
3093 | 177 | if py_val == NULL: | ||
3094 | 178 | raise KeyError("Key %s is not present" % key) | ||
3095 | 179 | val = <object>(py_val) | ||
3096 | 180 | return val | ||
3097 | 181 | |||
3098 | 182 | cdef int _insert_clean(self, PyObject *key) except -1: | ||
3099 | 183 | """Insert a key into self.table. | ||
3100 | 184 | |||
3101 | 185 | This is only meant to be used during times like '_resize', | ||
3102 | 186 | as it makes a lot of assuptions about keys not already being present, | ||
3103 | 187 | and there being no dummy entries. | ||
3104 | 188 | """ | ||
3105 | 189 | cdef size_t i, n_lookup | ||
3106 | 190 | cdef long the_hash | ||
3107 | 191 | cdef PyObject **table, **slot | ||
3108 | 192 | cdef Py_ssize_t mask | ||
3109 | 193 | |||
3110 | 194 | mask = self._mask | ||
3111 | 195 | table = self._table | ||
3112 | 196 | |||
3113 | 197 | the_hash = PyObject_Hash(key) | ||
3114 | 198 | i = the_hash | ||
3115 | 199 | for n_lookup from 0 <= n_lookup <= <size_t>mask: # Don't loop forever | ||
3116 | 200 | slot = &table[i & mask] | ||
3117 | 201 | if slot[0] == NULL: | ||
3118 | 202 | slot[0] = key | ||
3119 | 203 | self._fill = self._fill + 1 | ||
3120 | 204 | self._used = self._used + 1 | ||
3121 | 205 | return 1 | ||
3122 | 206 | i = i + 1 + n_lookup | ||
3123 | 207 | raise RuntimeError('ran out of slots.') | ||
3124 | 208 | |||
3125 | 209 | def _py_resize(self, min_used): | ||
3126 | 210 | """Do not use this directly, it is only exposed for testing.""" | ||
3127 | 211 | return self._resize(min_used) | ||
3128 | 212 | |||
3129 | 213 | cdef Py_ssize_t _resize(self, Py_ssize_t min_used) except -1: | ||
3130 | 214 | """Resize the internal table. | ||
3131 | 215 | |||
3132 | 216 | The final table will be big enough to hold at least min_used entries. | ||
3133 | 217 | We will copy the data from the existing table over, leaving out dummy | ||
3134 | 218 | entries. | ||
3135 | 219 | |||
3136 | 220 | :return: The new size of the internal table | ||
3137 | 221 | """ | ||
3138 | 222 | cdef Py_ssize_t new_size, n_bytes, remaining | ||
3139 | 223 | cdef PyObject **new_table, **old_table, **slot | ||
3140 | 224 | |||
3141 | 225 | new_size = DEFAULT_SIZE | ||
3142 | 226 | while new_size <= min_used and new_size > 0: | ||
3143 | 227 | new_size = new_size << 1 | ||
3144 | 228 | # We rolled over our signed size field | ||
3145 | 229 | if new_size <= 0: | ||
3146 | 230 | raise MemoryError() | ||
3147 | 231 | # Even if min_used == self._mask + 1, and we aren't changing the actual | ||
3148 | 232 | # size, we will still run the algorithm so that dummy entries are | ||
3149 | 233 | # removed | ||
3150 | 234 | # TODO: Test this | ||
3151 | 235 | # if new_size < self._used: | ||
3152 | 236 | # raise RuntimeError('cannot shrink SimpleSet to something' | ||
3153 | 237 | # ' smaller than the number of used slots.') | ||
3154 | 238 | n_bytes = sizeof(PyObject*) * new_size; | ||
3155 | 239 | new_table = <PyObject **>PyMem_Malloc(n_bytes) | ||
3156 | 240 | if new_table == NULL: | ||
3157 | 241 | raise MemoryError() | ||
3158 | 242 | |||
3159 | 243 | old_table = self._table | ||
3160 | 244 | self._table = new_table | ||
3161 | 245 | memset(self._table, 0, n_bytes) | ||
3162 | 246 | self._mask = new_size - 1 | ||
3163 | 247 | self._used = 0 | ||
3164 | 248 | remaining = self._fill | ||
3165 | 249 | self._fill = 0 | ||
3166 | 250 | |||
3167 | 251 | # Moving everything to the other table is refcount neutral, so we don't | ||
3168 | 252 | # worry about it. | ||
3169 | 253 | slot = old_table | ||
3170 | 254 | while remaining > 0: | ||
3171 | 255 | if slot[0] == NULL: # unused slot | ||
3172 | 256 | pass | ||
3173 | 257 | elif slot[0] == _dummy: # dummy slot | ||
3174 | 258 | remaining = remaining - 1 | ||
3175 | 259 | else: # active slot | ||
3176 | 260 | remaining = remaining - 1 | ||
3177 | 261 | self._insert_clean(slot[0]) | ||
3178 | 262 | slot = slot + 1 | ||
3179 | 263 | PyMem_Free(old_table) | ||
3180 | 264 | return new_size | ||
3181 | 265 | |||
3182 | 266 | def add(self, key): | ||
3183 | 267 | """Similar to set.add(), start tracking this key. | ||
3184 | 268 | |||
3185 | 269 | There is one small difference, which is that we return the object that | ||
3186 | 270 | is stored at the given location. (which is closer to the | ||
3187 | 271 | dict.setdefault() functionality.) | ||
3188 | 272 | """ | ||
3189 | 273 | return self._add(key) | ||
3190 | 274 | |||
3191 | 275 | cdef object _add(self, key): | ||
3192 | 276 | cdef PyObject **slot, *py_key | ||
3193 | 277 | cdef int added | ||
3194 | 278 | |||
3195 | 279 | py_key = <PyObject *>key | ||
3196 | 280 | if (Py_TYPE(py_key).tp_richcompare == NULL | ||
3197 | 281 | or Py_TYPE(py_key).tp_hash == NULL): | ||
3198 | 282 | raise TypeError('Types added to SimpleSet must implement' | ||
3199 | 283 | ' both tp_richcompare and tp_hash') | ||
3200 | 284 | added = 0 | ||
3201 | 285 | # We need at least one empty slot | ||
3202 | 286 | assert self._used < self._mask | ||
3203 | 287 | slot = _lookup(self, key) | ||
3204 | 288 | if (slot[0] == NULL): | ||
3205 | 289 | Py_INCREF(py_key) | ||
3206 | 290 | self._fill = self._fill + 1 | ||
3207 | 291 | self._used = self._used + 1 | ||
3208 | 292 | slot[0] = py_key | ||
3209 | 293 | added = 1 | ||
3210 | 294 | elif (slot[0] == _dummy): | ||
3211 | 295 | Py_INCREF(py_key) | ||
3212 | 296 | self._used = self._used + 1 | ||
3213 | 297 | slot[0] = py_key | ||
3214 | 298 | added = 1 | ||
3215 | 299 | # No else: clause. If _lookup returns a pointer to | ||
3216 | 300 | # a live object, then we already have a value at this location. | ||
3217 | 301 | retval = <object>(slot[0]) | ||
3218 | 302 | # PySet and PyDict use a 2-3rds full algorithm, we'll follow suit | ||
3219 | 303 | if added and (self._fill * 3) >= ((self._mask + 1) * 2): | ||
3220 | 304 | # However, we always work for a load factor of 2:1 | ||
3221 | 305 | self._resize(self._used * 2) | ||
3222 | 306 | # Even if we resized and ended up moving retval into a different slot, | ||
3223 | 307 | # it is still the value that is held at the slot equivalent to 'key', | ||
3224 | 308 | # so we can still return it | ||
3225 | 309 | return retval | ||
3226 | 310 | |||
3227 | 311 | def discard(self, key): | ||
3228 | 312 | """Remove key from the set, whether it exists or not. | ||
3229 | 313 | |||
3230 | 314 | :return: False if the item did not exist, True if it did | ||
3231 | 315 | """ | ||
3232 | 316 | if self._discard(key): | ||
3233 | 317 | return True | ||
3234 | 318 | return False | ||
3235 | 319 | |||
3236 | 320 | cdef int _discard(self, key) except -1: | ||
3237 | 321 | cdef PyObject **slot, *py_key | ||
3238 | 322 | |||
3239 | 323 | slot = _lookup(self, key) | ||
3240 | 324 | if slot[0] == NULL or slot[0] == _dummy: | ||
3241 | 325 | return 0 | ||
3242 | 326 | self._used = self._used - 1 | ||
3243 | 327 | Py_DECREF(slot[0]) | ||
3244 | 328 | slot[0] = _dummy | ||
3245 | 329 | # PySet uses the heuristic: If more than 1/5 are dummies, then resize | ||
3246 | 330 | # them away | ||
3247 | 331 | # if ((so->_fill - so->_used) * 5 < so->mask) | ||
3248 | 332 | # However, we are planning on using this as an interning structure, in | ||
3249 | 333 | # which we will be putting a lot of objects. And we expect that large | ||
3250 | 334 | # groups of them are going to have the same lifetime. | ||
3251 | 335 | # Dummy entries hurt a little bit because they cause the lookup to keep | ||
3252 | 336 | # searching, but resizing is also rather expensive | ||
3253 | 337 | # For now, we'll just use their algorithm, but we may want to revisit | ||
3254 | 338 | # it | ||
3255 | 339 | if ((self._fill - self._used) * 5 > self._mask): | ||
3256 | 340 | self._resize(self._used * 2) | ||
3257 | 341 | return 1 | ||
3258 | 342 | |||
3259 | 343 | def __iter__(self): | ||
3260 | 344 | return _SimpleSet_iterator(self) | ||
3261 | 345 | |||
3262 | 346 | |||
3263 | 347 | cdef class _SimpleSet_iterator: | ||
3264 | 348 | """Iterator over the SimpleSet structure.""" | ||
3265 | 349 | |||
3266 | 350 | cdef Py_ssize_t pos | ||
3267 | 351 | cdef SimpleSet set | ||
3268 | 352 | cdef Py_ssize_t _used # track if things have been mutated while iterating | ||
3269 | 353 | cdef Py_ssize_t len # number of entries left | ||
3270 | 354 | |||
3271 | 355 | def __init__(self, obj): | ||
3272 | 356 | self.set = obj | ||
3273 | 357 | self.pos = 0 | ||
3274 | 358 | self._used = self.set._used | ||
3275 | 359 | self.len = self.set._used | ||
3276 | 360 | |||
3277 | 361 | def __iter__(self): | ||
3278 | 362 | return self | ||
3279 | 363 | |||
3280 | 364 | def __next__(self): | ||
3281 | 365 | cdef Py_ssize_t mask, i | ||
3282 | 366 | cdef PyObject *key | ||
3283 | 367 | |||
3284 | 368 | if self.set is None: | ||
3285 | 369 | raise StopIteration | ||
3286 | 370 | if self.set._used != self._used: | ||
3287 | 371 | # Force this exception to continue to be raised | ||
3288 | 372 | self._used = -1 | ||
3289 | 373 | raise RuntimeError("Set size changed during iteration") | ||
3290 | 374 | if not SimpleSet_Next(self.set, &self.pos, &key): | ||
3291 | 375 | self.set = None | ||
3292 | 376 | raise StopIteration | ||
3293 | 377 | # we found something | ||
3294 | 378 | the_key = <object>key # INCREF | ||
3295 | 379 | self.len = self.len - 1 | ||
3296 | 380 | return the_key | ||
3297 | 381 | |||
3298 | 382 | def __length_hint__(self): | ||
3299 | 383 | if self.set is not None and self._used == self.set._used: | ||
3300 | 384 | return self.len | ||
3301 | 385 | return 0 | ||
3302 | 386 | |||
3303 | 387 | |||
3304 | 388 | |||
3305 | 389 | cdef api SimpleSet SimpleSet_New(): | ||
3306 | 390 | """Create a new SimpleSet object.""" | ||
3307 | 391 | return SimpleSet() | ||
3308 | 392 | |||
3309 | 393 | |||
3310 | 394 | cdef SimpleSet _check_self(object self): | ||
3311 | 395 | """Check that the parameter is not None. | ||
3312 | 396 | |||
3313 | 397 | Pyrex/Cython will do type checking, but only to ensure that an object is | ||
3314 | 398 | either the right type or None. You can say "object foo not None" for pure | ||
3315 | 399 | python functions, but not for C functions. | ||
3316 | 400 | So this is just a helper for all the apis that need to do the check. | ||
3317 | 401 | """ | ||
3318 | 402 | cdef SimpleSet true_self | ||
3319 | 403 | if self is None: | ||
3320 | 404 | raise TypeError('self must not be None') | ||
3321 | 405 | true_self = self | ||
3322 | 406 | return true_self | ||
3323 | 407 | |||
3324 | 408 | |||
3325 | 409 | cdef PyObject **_lookup(SimpleSet self, object key) except NULL: | ||
3326 | 410 | """Find the slot where 'key' would fit. | ||
3327 | 411 | |||
3328 | 412 | This is the same as a dicts 'lookup' function. | ||
3329 | 413 | |||
3330 | 414 | :param key: An object we are looking up | ||
3331 | 415 | :param hash: The hash for key | ||
3332 | 416 | :return: The location in self.table where key should be put. | ||
3333 | 417 | location == NULL is an exception, but (*location) == NULL just | ||
3334 | 418 | indicates the slot is empty and can be used. | ||
3335 | 419 | """ | ||
3336 | 420 | # This uses Quadratic Probing: | ||
3337 | 421 | # http://en.wikipedia.org/wiki/Quadratic_probing | ||
3338 | 422 | # with c1 = c2 = 1/2 | ||
3339 | 423 | # This leads to probe locations at: | ||
3340 | 424 | # h0 = hash(k1) | ||
3341 | 425 | # h1 = h0 + 1 | ||
3342 | 426 | # h2 = h0 + 3 = h1 + 1 + 1 | ||
3343 | 427 | # h3 = h0 + 6 = h2 + 1 + 2 | ||
3344 | 428 | # h4 = h0 + 10 = h2 + 1 + 3 | ||
3345 | 429 | # Note that all of these are '& mask', but that is computed *after* the | ||
3346 | 430 | # offset. | ||
3347 | 431 | # This differs from the algorithm used by Set and Dict. Which, effectively, | ||
3348 | 432 | # use double-hashing, and a step size that starts large, but dwindles to | ||
3349 | 433 | # stepping one-by-one. | ||
3350 | 434 | # This gives more 'locality' in that if you have a collision at offset X, | ||
3351 | 435 | # the first fallback is X+1, which is fast to check. However, that means | ||
3352 | 436 | # that an object w/ hash X+1 will also check there, and then X+2 next. | ||
3353 | 437 | # However, for objects with differing hashes, their chains are different. | ||
3354 | 438 | # The former checks X, X+1, X+3, ... the latter checks X+1, X+2, X+4, ... | ||
3355 | 439 | # So different hashes diverge quickly. | ||
3356 | 440 | # A bigger problem is that we *only* ever use the lowest bits of the hash | ||
3357 | 441 | # So all integers (x + SIZE*N) will resolve into the same bucket, and all | ||
3358 | 442 | # use the same collision resolution. We may want to try to find a way to | ||
3359 | 443 | # incorporate the upper bits of the hash with quadratic probing. (For | ||
3360 | 444 | # example, X, X+1, X+3+some_upper_bits, X+6+more_upper_bits, etc.) | ||
3361 | 445 | cdef size_t i, n_lookup | ||
3362 | 446 | cdef Py_ssize_t mask | ||
3363 | 447 | cdef long key_hash | ||
3364 | 448 | cdef PyObject **table, **slot, *cur, **free_slot, *py_key | ||
3365 | 449 | |||
3366 | 450 | py_key = <PyObject *>key | ||
3367 | 451 | # Note: avoid using hash(obj) because of a bug w/ pyrex 0.9.8.5 and 64-bit | ||
3368 | 452 | # (it treats hash() as returning an 'int' rather than a 'long') | ||
3369 | 453 | key_hash = PyObject_Hash(py_key) | ||
3370 | 454 | i = <size_t>key_hash | ||
3371 | 455 | mask = self._mask | ||
3372 | 456 | table = self._table | ||
3373 | 457 | free_slot = NULL | ||
3374 | 458 | for n_lookup from 0 <= n_lookup <= <size_t>mask: # Don't loop forever | ||
3375 | 459 | slot = &table[i & mask] | ||
3376 | 460 | cur = slot[0] | ||
3377 | 461 | if cur == NULL: | ||
3378 | 462 | # Found a blank spot | ||
3379 | 463 | if free_slot != NULL: | ||
3380 | 464 | # Did we find an earlier _dummy entry? | ||
3381 | 465 | return free_slot | ||
3382 | 466 | else: | ||
3383 | 467 | return slot | ||
3384 | 468 | if cur == py_key: | ||
3385 | 469 | # Found an exact pointer to the key | ||
3386 | 470 | return slot | ||
3387 | 471 | if cur == _dummy: | ||
3388 | 472 | if free_slot == NULL: | ||
3389 | 473 | free_slot = slot | ||
3390 | 474 | elif _is_equal(py_key, key_hash, cur): | ||
3391 | 475 | # Both py_key and cur belong in this slot, return it | ||
3392 | 476 | return slot | ||
3393 | 477 | i = i + 1 + n_lookup | ||
3394 | 478 | raise AssertionError('should never get here') | ||
3395 | 479 | |||
3396 | 480 | |||
3397 | 481 | cdef api PyObject **_SimpleSet_Lookup(object self, object key) except NULL: | ||
3398 | 482 | """Find the slot where 'key' would fit. | ||
3399 | 483 | |||
3400 | 484 | This is the same as a dicts 'lookup' function. This is a private | ||
3401 | 485 | api because mutating what you get without maintaing the other invariants | ||
3402 | 486 | is a 'bad thing'. | ||
3403 | 487 | |||
3404 | 488 | :param key: An object we are looking up | ||
3405 | 489 | :param hash: The hash for key | ||
3406 | 490 | :return: The location in self._table where key should be put | ||
3407 | 491 | should never be NULL, but may reference a NULL (PyObject*) | ||
3408 | 492 | """ | ||
3409 | 493 | return _lookup(_check_self(self), key) | ||
3410 | 494 | |||
3411 | 495 | |||
3412 | 496 | cdef api object SimpleSet_Add(object self, object key): | ||
3413 | 497 | """Add a key to the SimpleSet (set). | ||
3414 | 498 | |||
3415 | 499 | :param self: The SimpleSet to add the key to. | ||
3416 | 500 | :param key: The key to be added. If the key is already present, | ||
3417 | 501 | self will not be modified | ||
3418 | 502 | :return: The current key stored at the location defined by 'key'. | ||
3419 | 503 | This may be the same object, or it may be an equivalent object. | ||
3420 | 504 | (consider dict.setdefault(key, key)) | ||
3421 | 505 | """ | ||
3422 | 506 | return _check_self(self)._add(key) | ||
3423 | 507 | |||
3424 | 508 | |||
3425 | 509 | cdef api int SimpleSet_Contains(object self, object key) except -1: | ||
3426 | 510 | """Is key present in self?""" | ||
3427 | 511 | return (key in _check_self(self)) | ||
3428 | 512 | |||
3429 | 513 | |||
3430 | 514 | cdef api int SimpleSet_Discard(object self, object key) except -1: | ||
3431 | 515 | """Remove the object referenced at location 'key'. | ||
3432 | 516 | |||
3433 | 517 | :param self: The SimpleSet being modified | ||
3434 | 518 | :param key: The key we are checking on | ||
3435 | 519 | :return: 1 if there was an object present, 0 if there was not, and -1 on | ||
3436 | 520 | error. | ||
3437 | 521 | """ | ||
3438 | 522 | return _check_self(self)._discard(key) | ||
3439 | 523 | |||
3440 | 524 | |||
3441 | 525 | cdef api PyObject *SimpleSet_Get(SimpleSet self, object key) except? NULL: | ||
3442 | 526 | """Get a pointer to the object present at location 'key'. | ||
3443 | 527 | |||
3444 | 528 | This returns an object which is equal to key which was previously added to | ||
3445 | 529 | self. This returns a borrowed reference, as it may also return NULL if no | ||
3446 | 530 | value is present at that location. | ||
3447 | 531 | |||
3448 | 532 | :param key: The value we are looking for | ||
3449 | 533 | :return: The object present at that location | ||
3450 | 534 | """ | ||
3451 | 535 | return _check_self(self)._get(key) | ||
3452 | 536 | |||
3453 | 537 | |||
3454 | 538 | cdef api Py_ssize_t SimpleSet_Size(object self) except -1: | ||
3455 | 539 | """Get the number of active entries in 'self'""" | ||
3456 | 540 | return _check_self(self)._used | ||
3457 | 541 | |||
3458 | 542 | |||
3459 | 543 | cdef api int SimpleSet_Next(object self, Py_ssize_t *pos, | ||
3460 | 544 | PyObject **key) except -1: | ||
3461 | 545 | """Walk over items in a SimpleSet. | ||
3462 | 546 | |||
3463 | 547 | :param pos: should be initialized to 0 by the caller, and will be updated | ||
3464 | 548 | by this function | ||
3465 | 549 | :param key: Will return a borrowed reference to key | ||
3466 | 550 | :return: 0 if nothing left, 1 if we are returning a new value | ||
3467 | 551 | """ | ||
3468 | 552 | cdef Py_ssize_t i, mask | ||
3469 | 553 | cdef SimpleSet true_self | ||
3470 | 554 | cdef PyObject **table | ||
3471 | 555 | true_self = _check_self(self) | ||
3472 | 556 | i = pos[0] | ||
3473 | 557 | if (i < 0): | ||
3474 | 558 | return 0 | ||
3475 | 559 | mask = true_self._mask | ||
3476 | 560 | table= true_self._table | ||
3477 | 561 | while (i <= mask and (table[i] == NULL or table[i] == _dummy)): | ||
3478 | 562 | i = i + 1 | ||
3479 | 563 | pos[0] = i + 1 | ||
3480 | 564 | if (i > mask): | ||
3481 | 565 | return 0 # All done | ||
3482 | 566 | if (key != NULL): | ||
3483 | 567 | key[0] = table[i] | ||
3484 | 568 | return 1 | ||
3485 | 569 | |||
3486 | 570 | |||
3487 | 571 | cdef int SimpleSet_traverse(SimpleSet self, visitproc visit, | ||
3488 | 572 | void *arg) except -1: | ||
3489 | 573 | """This is an implementation of 'tp_traverse' that hits the whole table. | ||
3490 | 574 | |||
3491 | 575 | Cython/Pyrex don't seem to let you define a tp_traverse, and they only | ||
3492 | 576 | define one for you if you have an 'object' attribute. Since they don't | ||
3493 | 577 | support C arrays of objects, we access the PyObject * directly. | ||
3494 | 578 | """ | ||
3495 | 579 | cdef Py_ssize_t pos | ||
3496 | 580 | cdef PyObject *next_key | ||
3497 | 581 | cdef int ret | ||
3498 | 582 | |||
3499 | 583 | pos = 0 | ||
3500 | 584 | while SimpleSet_Next(self, &pos, &next_key): | ||
3501 | 585 | ret = visit(next_key, arg) | ||
3502 | 586 | if ret: | ||
3503 | 587 | return ret | ||
3504 | 588 | return 0 | ||
3505 | 589 | |||
3506 | 590 | # It is a little bit ugly to do this, but it works, and means that Meliae can | ||
3507 | 591 | # dump the total memory consumed by all child objects. | ||
3508 | 592 | (<PyTypeObject *>SimpleSet).tp_traverse = <traverseproc>SimpleSet_traverse | ||
3509 | 0 | 593 | ||
3510 | === added file 'bzrlib/_static_tuple_c.c' | |||
3511 | --- bzrlib/_static_tuple_c.c 1970-01-01 00:00:00 +0000 | |||
3512 | +++ bzrlib/_static_tuple_c.c 2010-02-18 00:00:52 +0000 | |||
3513 | @@ -0,0 +1,945 @@ | |||
3514 | 1 | /* Copyright (C) 2009, 2010 Canonical Ltd | ||
3515 | 2 | * | ||
3516 | 3 | * This program is free software; you can redistribute it and/or modify | ||
3517 | 4 | * it under the terms of the GNU General Public License as published by | ||
3518 | 5 | * the Free Software Foundation; either version 2 of the License, or | ||
3519 | 6 | * (at your option) any later version. | ||
3520 | 7 | * | ||
3521 | 8 | * This program is distributed in the hope that it will be useful, | ||
3522 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3523 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3524 | 11 | * GNU General Public License for more details. | ||
3525 | 12 | * | ||
3526 | 13 | * You should have received a copy of the GNU General Public License | ||
3527 | 14 | * along with this program; if not, write to the Free Software | ||
3528 | 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
3529 | 16 | */ | ||
3530 | 17 | |||
3531 | 18 | /* Must be defined before importing _static_tuple_c.h so that we get the right | ||
3532 | 19 | * linkage. | ||
3533 | 20 | */ | ||
3534 | 21 | #define STATIC_TUPLE_MODULE | ||
3535 | 22 | |||
3536 | 23 | #include <Python.h> | ||
3537 | 24 | #include "python-compat.h" | ||
3538 | 25 | |||
3539 | 26 | #include "_static_tuple_c.h" | ||
3540 | 27 | #include "_export_c_api.h" | ||
3541 | 28 | |||
3542 | 29 | /* Pyrex 0.9.6.4 exports _simple_set_pyx_api as | ||
3543 | 30 | * import__simple_set_pyx(), while Pyrex 0.9.8.5 and Cython 0.11.3 export them | ||
3544 | 31 | * as import_bzrlib___simple_set_pyx(). As such, we just #define one to be | ||
3545 | 32 | * equivalent to the other in our internal code. | ||
3546 | 33 | */ | ||
3547 | 34 | #define import__simple_set_pyx import_bzrlib___simple_set_pyx | ||
3548 | 35 | #include "_simple_set_pyx_api.h" | ||
3549 | 36 | |||
3550 | 37 | #if defined(__GNUC__) | ||
3551 | 38 | # define inline __inline__ | ||
3552 | 39 | #elif defined(_MSC_VER) | ||
3553 | 40 | # define inline __inline | ||
3554 | 41 | #else | ||
3555 | 42 | # define inline | ||
3556 | 43 | #endif | ||
3557 | 44 | |||
3558 | 45 | |||
3559 | 46 | /* The one and only StaticTuple with no values */ | ||
3560 | 47 | static StaticTuple *_empty_tuple = NULL; | ||
3561 | 48 | static PyObject *_interned_tuples = NULL; | ||
3562 | 49 | |||
3563 | 50 | |||
3564 | 51 | static inline int | ||
3565 | 52 | _StaticTuple_is_interned(StaticTuple *self) | ||
3566 | 53 | { | ||
3567 | 54 | return self->flags & STATIC_TUPLE_INTERNED_FLAG; | ||
3568 | 55 | } | ||
3569 | 56 | |||
3570 | 57 | |||
3571 | 58 | |||
3572 | 59 | static PyObject * | ||
3573 | 60 | StaticTuple_as_tuple(StaticTuple *self) | ||
3574 | 61 | { | ||
3575 | 62 | PyObject *tpl = NULL, *obj = NULL; | ||
3576 | 63 | int i, len; | ||
3577 | 64 | |||
3578 | 65 | len = self->size; | ||
3579 | 66 | tpl = PyTuple_New(len); | ||
3580 | 67 | if (!tpl) { | ||
3581 | 68 | /* Malloc failure */ | ||
3582 | 69 | return NULL; | ||
3583 | 70 | } | ||
3584 | 71 | for (i = 0; i < len; ++i) { | ||
3585 | 72 | obj = (PyObject *)self->items[i]; | ||
3586 | 73 | Py_INCREF(obj); | ||
3587 | 74 | PyTuple_SET_ITEM(tpl, i, obj); | ||
3588 | 75 | } | ||
3589 | 76 | return tpl; | ||
3590 | 77 | } | ||
3591 | 78 | |||
3592 | 79 | |||
3593 | 80 | static char StaticTuple_as_tuple_doc[] = "as_tuple() => tuple"; | ||
3594 | 81 | |||
3595 | 82 | static StaticTuple * | ||
3596 | 83 | StaticTuple_Intern(StaticTuple *self) | ||
3597 | 84 | { | ||
3598 | 85 | PyObject *canonical_tuple = NULL; | ||
3599 | 86 | |||
3600 | 87 | if (_interned_tuples == NULL || _StaticTuple_is_interned(self)) { | ||
3601 | 88 | Py_INCREF(self); | ||
3602 | 89 | return self; | ||
3603 | 90 | } | ||
3604 | 91 | /* SimpleSet_Add returns whatever object is present at self | ||
3605 | 92 | * or the new object if it needs to add it. | ||
3606 | 93 | */ | ||
3607 | 94 | canonical_tuple = SimpleSet_Add(_interned_tuples, (PyObject *)self); | ||
3608 | 95 | if (!canonical_tuple) { | ||
3609 | 96 | // Some sort of exception, propogate it. | ||
3610 | 97 | return NULL; | ||
3611 | 98 | } | ||
3612 | 99 | if (canonical_tuple != (PyObject *)self) { | ||
3613 | 100 | // There was already a tuple with that value | ||
3614 | 101 | return (StaticTuple *)canonical_tuple; | ||
3615 | 102 | } | ||
3616 | 103 | self->flags |= STATIC_TUPLE_INTERNED_FLAG; | ||
3617 | 104 | // The two references in the dict do not count, so that the StaticTuple | ||
3618 | 105 | // object does not become immortal just because it was interned. | ||
3619 | 106 | Py_REFCNT(self) -= 1; | ||
3620 | 107 | return self; | ||
3621 | 108 | } | ||
3622 | 109 | |||
3623 | 110 | static char StaticTuple_Intern_doc[] = "intern() => unique StaticTuple\n" | ||
3624 | 111 | "Return a 'canonical' StaticTuple object.\n" | ||
3625 | 112 | "Similar to intern() for strings, this makes sure there\n" | ||
3626 | 113 | "is only one StaticTuple object for a given value\n." | ||
3627 | 114 | "Common usage is:\n" | ||
3628 | 115 | " key = StaticTuple('foo', 'bar').intern()\n"; | ||
3629 | 116 | |||
3630 | 117 | |||
3631 | 118 | static void | ||
3632 | 119 | StaticTuple_dealloc(StaticTuple *self) | ||
3633 | 120 | { | ||
3634 | 121 | int i, len; | ||
3635 | 122 | |||
3636 | 123 | if (_StaticTuple_is_interned(self)) { | ||
3637 | 124 | /* revive dead object temporarily for Discard */ | ||
3638 | 125 | Py_REFCNT(self) = 2; | ||
3639 | 126 | if (SimpleSet_Discard(_interned_tuples, (PyObject*)self) != 1) | ||
3640 | 127 | Py_FatalError("deletion of interned StaticTuple failed"); | ||
3641 | 128 | self->flags &= ~STATIC_TUPLE_INTERNED_FLAG; | ||
3642 | 129 | } | ||
3643 | 130 | len = self->size; | ||
3644 | 131 | for (i = 0; i < len; ++i) { | ||
3645 | 132 | Py_XDECREF(self->items[i]); | ||
3646 | 133 | } | ||
3647 | 134 | Py_TYPE(self)->tp_free((PyObject *)self); | ||
3648 | 135 | } | ||
3649 | 136 | |||
3650 | 137 | |||
3651 | 138 | /* Similar to PyTuple_New() */ | ||
3652 | 139 | static StaticTuple * | ||
3653 | 140 | StaticTuple_New(Py_ssize_t size) | ||
3654 | 141 | { | ||
3655 | 142 | StaticTuple *stuple; | ||
3656 | 143 | |||
3657 | 144 | if (size < 0 || size > 255) { | ||
3658 | 145 | /* Too big or too small */ | ||
3659 | 146 | PyErr_SetString(PyExc_ValueError, "StaticTuple(...)" | ||
3660 | 147 | " takes from 0 to 255 items"); | ||
3661 | 148 | return NULL; | ||
3662 | 149 | } | ||
3663 | 150 | if (size == 0 && _empty_tuple != NULL) { | ||
3664 | 151 | Py_INCREF(_empty_tuple); | ||
3665 | 152 | return _empty_tuple; | ||
3666 | 153 | } | ||
3667 | 154 | /* Note that we use PyObject_NewVar because we want to allocate a variable | ||
3668 | 155 | * width entry. However we *aren't* truly a PyVarObject because we don't | ||
3669 | 156 | * use a long for ob_size. Instead we use a plain 'size' that is an int, | ||
3670 | 157 | * and will be overloaded with flags in the future. | ||
3671 | 158 | * As such we do the alloc, and then have to clean up anything it does | ||
3672 | 159 | * incorrectly. | ||
3673 | 160 | */ | ||
3674 | 161 | stuple = PyObject_NewVar(StaticTuple, &StaticTuple_Type, size); | ||
3675 | 162 | if (stuple == NULL) { | ||
3676 | 163 | return NULL; | ||
3677 | 164 | } | ||
3678 | 165 | stuple->size = size; | ||
3679 | 166 | stuple->flags = 0; | ||
3680 | 167 | stuple->_unused0 = 0; | ||
3681 | 168 | stuple->_unused1 = 0; | ||
3682 | 169 | if (size > 0) { | ||
3683 | 170 | memset(stuple->items, 0, sizeof(PyObject *) * size); | ||
3684 | 171 | } | ||
3685 | 172 | #if STATIC_TUPLE_HAS_HASH | ||
3686 | 173 | stuple->hash = -1; | ||
3687 | 174 | #endif | ||
3688 | 175 | return stuple; | ||
3689 | 176 | } | ||
3690 | 177 | |||
3691 | 178 | |||
3692 | 179 | static StaticTuple * | ||
3693 | 180 | StaticTuple_FromSequence(PyObject *sequence) | ||
3694 | 181 | { | ||
3695 | 182 | StaticTuple *new = NULL; | ||
3696 | 183 | PyObject *as_tuple = NULL; | ||
3697 | 184 | PyObject *item; | ||
3698 | 185 | Py_ssize_t i, size; | ||
3699 | 186 | |||
3700 | 187 | if (StaticTuple_CheckExact(sequence)) { | ||
3701 | 188 | Py_INCREF(sequence); | ||
3702 | 189 | return (StaticTuple *)sequence; | ||
3703 | 190 | } | ||
3704 | 191 | if (!PySequence_Check(sequence)) { | ||
3705 | 192 | as_tuple = PySequence_Tuple(sequence); | ||
3706 | 193 | if (as_tuple == NULL) | ||
3707 | 194 | goto done; | ||
3708 | 195 | sequence = as_tuple; | ||
3709 | 196 | } | ||
3710 | 197 | size = PySequence_Size(sequence); | ||
3711 | 198 | if (size == -1) { | ||
3712 | 199 | goto done; | ||
3713 | 200 | } | ||
3714 | 201 | new = StaticTuple_New(size); | ||
3715 | 202 | if (new == NULL) { | ||
3716 | 203 | goto done; | ||
3717 | 204 | } | ||
3718 | 205 | for (i = 0; i < size; ++i) { | ||
3719 | 206 | // This returns a new reference, which we then 'steal' with | ||
3720 | 207 | // StaticTuple_SET_ITEM | ||
3721 | 208 | item = PySequence_GetItem(sequence, i); | ||
3722 | 209 | if (item == NULL) { | ||
3723 | 210 | Py_DECREF(new); | ||
3724 | 211 | new = NULL; | ||
3725 | 212 | goto done; | ||
3726 | 213 | } | ||
3727 | 214 | StaticTuple_SET_ITEM(new, i, item); | ||
3728 | 215 | } | ||
3729 | 216 | done: | ||
3730 | 217 | Py_XDECREF(as_tuple); | ||
3731 | 218 | return (StaticTuple *)new; | ||
3732 | 219 | } | ||
3733 | 220 | |||
3734 | 221 | static StaticTuple * | ||
3735 | 222 | StaticTuple_from_sequence(PyObject *self, PyObject *args, PyObject *kwargs) | ||
3736 | 223 | { | ||
3737 | 224 | PyObject *sequence; | ||
3738 | 225 | if (!PyArg_ParseTuple(args, "O", &sequence)) | ||
3739 | 226 | return NULL; | ||
3740 | 227 | return StaticTuple_FromSequence(sequence); | ||
3741 | 228 | } | ||
3742 | 229 | |||
3743 | 230 | |||
3744 | 231 | /* Check that all items we point to are 'valid' */ | ||
3745 | 232 | static int | ||
3746 | 233 | StaticTuple_check_items(StaticTuple *self) | ||
3747 | 234 | { | ||
3748 | 235 | int i; | ||
3749 | 236 | PyObject *obj; | ||
3750 | 237 | |||
3751 | 238 | for (i = 0; i < self->size; ++i) { | ||
3752 | 239 | obj = self->items[i]; | ||
3753 | 240 | if (obj == NULL) { | ||
3754 | 241 | PyErr_SetString(PyExc_RuntimeError, "StaticTuple(...)" | ||
3755 | 242 | " should not have a NULL entry."); | ||
3756 | 243 | return 0; | ||
3757 | 244 | } | ||
3758 | 245 | if (PyString_CheckExact(obj) | ||
3759 | 246 | || StaticTuple_CheckExact(obj) | ||
3760 | 247 | || obj == Py_None | ||
3761 | 248 | || PyBool_Check(obj) | ||
3762 | 249 | || PyInt_CheckExact(obj) | ||
3763 | 250 | || PyLong_CheckExact(obj) | ||
3764 | 251 | || PyFloat_CheckExact(obj) | ||
3765 | 252 | || PyUnicode_CheckExact(obj) | ||
3766 | 253 | ) continue; | ||
3767 | 254 | PyErr_Format(PyExc_TypeError, "StaticTuple(...)" | ||
3768 | 255 | " requires that all items are one of" | ||
3769 | 256 | " str, StaticTuple, None, bool, int, long, float, or unicode" | ||
3770 | 257 | " not %s.", Py_TYPE(obj)->tp_name); | ||
3771 | 258 | return 0; | ||
3772 | 259 | } | ||
3773 | 260 | return 1; | ||
3774 | 261 | } | ||
3775 | 262 | |||
3776 | 263 | static PyObject * | ||
3777 | 264 | StaticTuple_new_constructor(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||
3778 | 265 | { | ||
3779 | 266 | StaticTuple *self; | ||
3780 | 267 | PyObject *obj = NULL; | ||
3781 | 268 | Py_ssize_t i, len = 0; | ||
3782 | 269 | |||
3783 | 270 | if (type != &StaticTuple_Type) { | ||
3784 | 271 | PyErr_SetString(PyExc_TypeError, "we only support creating StaticTuple"); | ||
3785 | 272 | return NULL; | ||
3786 | 273 | } | ||
3787 | 274 | if (!PyTuple_CheckExact(args)) { | ||
3788 | 275 | PyErr_SetString(PyExc_TypeError, "args must be a tuple"); | ||
3789 | 276 | return NULL; | ||
3790 | 277 | } | ||
3791 | 278 | len = PyTuple_GET_SIZE(args); | ||
3792 | 279 | if (len < 0 || len > 255) { | ||
3793 | 280 | /* Check the length here so we can raise a TypeError instead of | ||
3794 | 281 | * StaticTuple_New's ValueError. | ||
3795 | 282 | */ | ||
3796 | 283 | PyErr_SetString(PyExc_TypeError, "StaticTuple(...)" | ||
3797 | 284 | " takes from 0 to 255 items"); | ||
3798 | 285 | return NULL; | ||
3799 | 286 | } | ||
3800 | 287 | self = (StaticTuple *)StaticTuple_New(len); | ||
3801 | 288 | if (self == NULL) { | ||
3802 | 289 | return NULL; | ||
3803 | 290 | } | ||
3804 | 291 | for (i = 0; i < len; ++i) { | ||
3805 | 292 | obj = PyTuple_GET_ITEM(args, i); | ||
3806 | 293 | Py_INCREF(obj); | ||
3807 | 294 | self->items[i] = obj; | ||
3808 | 295 | } | ||
3809 | 296 | if (!StaticTuple_check_items(self)) { | ||
3810 | 297 | type->tp_dealloc((PyObject *)self); | ||
3811 | 298 | return NULL; | ||
3812 | 299 | } | ||
3813 | 300 | return (PyObject *)self; | ||
3814 | 301 | } | ||
3815 | 302 | |||
3816 | 303 | static PyObject * | ||
3817 | 304 | StaticTuple_repr(StaticTuple *self) | ||
3818 | 305 | { | ||
3819 | 306 | PyObject *as_tuple, *tuple_repr, *result; | ||
3820 | 307 | |||
3821 | 308 | as_tuple = StaticTuple_as_tuple(self); | ||
3822 | 309 | if (as_tuple == NULL) { | ||
3823 | 310 | return NULL; | ||
3824 | 311 | } | ||
3825 | 312 | tuple_repr = PyObject_Repr(as_tuple); | ||
3826 | 313 | Py_DECREF(as_tuple); | ||
3827 | 314 | if (tuple_repr == NULL) { | ||
3828 | 315 | return NULL; | ||
3829 | 316 | } | ||
3830 | 317 | result = PyString_FromFormat("StaticTuple%s", | ||
3831 | 318 | PyString_AsString(tuple_repr)); | ||
3832 | 319 | return result; | ||
3833 | 320 | } | ||
3834 | 321 | |||
3835 | 322 | static long | ||
3836 | 323 | StaticTuple_hash(StaticTuple *self) | ||
3837 | 324 | { | ||
3838 | 325 | /* adapted from tuplehash(), is the specific hash value considered | ||
3839 | 326 | * 'stable'? | ||
3840 | 327 | */ | ||
3841 | 328 | register long x, y; | ||
3842 | 329 | Py_ssize_t len = self->size; | ||
3843 | 330 | PyObject **p; | ||
3844 | 331 | long mult = 1000003L; | ||
3845 | 332 | |||
3846 | 333 | #if STATIC_TUPLE_HAS_HASH | ||
3847 | 334 | if (self->hash != -1) { | ||
3848 | 335 | return self->hash; | ||
3849 | 336 | } | ||
3850 | 337 | #endif | ||
3851 | 338 | x = 0x345678L; | ||
3852 | 339 | p = self->items; | ||
3853 | 340 | // TODO: We could set specific flags if we know that, for example, all the | ||
3854 | 341 | // items are strings. I haven't seen a real-world benefit to that | ||
3855 | 342 | // yet, though. | ||
3856 | 343 | while (--len >= 0) { | ||
3857 | 344 | y = PyObject_Hash(*p++); | ||
3858 | 345 | if (y == -1) /* failure */ | ||
3859 | 346 | return -1; | ||
3860 | 347 | x = (x ^ y) * mult; | ||
3861 | 348 | /* the cast might truncate len; that doesn't change hash stability */ | ||
3862 | 349 | mult += (long)(82520L + len + len); | ||
3863 | 350 | } | ||
3864 | 351 | x += 97531L; | ||
3865 | 352 | if (x == -1) | ||
3866 | 353 | x = -2; | ||
3867 | 354 | #if STATIC_TUPLE_HAS_HASH | ||
3868 | 355 | self->hash = x; | ||
3869 | 356 | #endif | ||
3870 | 357 | return x; | ||
3871 | 358 | } | ||
3872 | 359 | |||
3873 | 360 | static PyObject * | ||
3874 | 361 | StaticTuple_richcompare_to_tuple(StaticTuple *v, PyObject *wt, int op) | ||
3875 | 362 | { | ||
3876 | 363 | PyObject *vt; | ||
3877 | 364 | PyObject *result = NULL; | ||
3878 | 365 | |||
3879 | 366 | vt = StaticTuple_as_tuple((StaticTuple *)v); | ||
3880 | 367 | if (vt == NULL) { | ||
3881 | 368 | goto done; | ||
3882 | 369 | } | ||
3883 | 370 | if (!PyTuple_Check(wt)) { | ||
3884 | 371 | PyErr_BadInternalCall(); | ||
3885 | 372 | goto done; | ||
3886 | 373 | } | ||
3887 | 374 | /* Now we have 2 tuples to compare, do it */ | ||
3888 | 375 | result = PyTuple_Type.tp_richcompare(vt, wt, op); | ||
3889 | 376 | done: | ||
3890 | 377 | Py_XDECREF(vt); | ||
3891 | 378 | return result; | ||
3892 | 379 | } | ||
3893 | 380 | |||
3894 | 381 | /** Compare two objects to determine if they are equivalent. | ||
3895 | 382 | * The basic flow is as follows | ||
3896 | 383 | * 1) First make sure that both objects are StaticTuple instances. If they | ||
3897 | 384 | * aren't then cast self to a tuple, and have the tuple do the comparison. | ||
3898 | 385 | * 2) Special case comparison to Py_None, because it happens to occur fairly | ||
3899 | 386 | * often in the test suite. | ||
3900 | 387 | * 3) Special case when v and w are the same pointer. As we know the answer to | ||
3901 | 388 | * all queries without walking individual items. | ||
3902 | 389 | * 4) For all operations, we then walk the items to find the first paired | ||
3903 | 390 | * items that are not equal. | ||
3904 | 391 | * 5) If all items found are equal, we then check the length of self and | ||
3905 | 392 | * other to determine equality. | ||
3906 | 393 | * 6) If an item differs, then we apply "op" to those last two items. (eg. | ||
3907 | 394 | * StaticTuple(A, B) > StaticTuple(A, C) iff B > C) | ||
3908 | 395 | */ | ||
3909 | 396 | |||
3910 | 397 | static PyObject * | ||
3911 | 398 | StaticTuple_richcompare(PyObject *v, PyObject *w, int op) | ||
3912 | 399 | { | ||
3913 | 400 | StaticTuple *v_st, *w_st; | ||
3914 | 401 | Py_ssize_t vlen, wlen, min_len, i; | ||
3915 | 402 | PyObject *v_obj, *w_obj; | ||
3916 | 403 | richcmpfunc string_richcompare; | ||
3917 | 404 | |||
3918 | 405 | if (!StaticTuple_CheckExact(v)) { | ||
3919 | 406 | /* This has never triggered, according to python-dev it seems this | ||
3920 | 407 | * might trigger if '__op__' is defined but '__rop__' is not, sort of | ||
3921 | 408 | * case. Such as "None == StaticTuple()" | ||
3922 | 409 | */ | ||
3923 | 410 | fprintf(stderr, "self is not StaticTuple\n"); | ||
3924 | 411 | Py_INCREF(Py_NotImplemented); | ||
3925 | 412 | return Py_NotImplemented; | ||
3926 | 413 | } | ||
3927 | 414 | v_st = (StaticTuple *)v; | ||
3928 | 415 | if (StaticTuple_CheckExact(w)) { | ||
3929 | 416 | /* The most common case */ | ||
3930 | 417 | w_st = (StaticTuple*)w; | ||
3931 | 418 | } else if (PyTuple_Check(w)) { | ||
3932 | 419 | /* One of v or w is a tuple, so we go the 'slow' route and cast up to | ||
3933 | 420 | * tuples to compare. | ||
3934 | 421 | */ | ||
3935 | 422 | /* TODO: This seems to be triggering more than I thought it would... | ||
3936 | 423 | * We probably want to optimize comparing self to other when | ||
3937 | 424 | * other is a tuple. | ||
3938 | 425 | */ | ||
3939 | 426 | return StaticTuple_richcompare_to_tuple(v_st, w, op); | ||
3940 | 427 | } else if (w == Py_None) { | ||
3941 | 428 | // None is always less than the object | ||
3942 | 429 | switch (op) { | ||
3943 | 430 | case Py_NE:case Py_GT:case Py_GE: | ||
3944 | 431 | Py_INCREF(Py_True); | ||
3945 | 432 | return Py_True; | ||
3946 | 433 | case Py_EQ:case Py_LT:case Py_LE: | ||
3947 | 434 | Py_INCREF(Py_False); | ||
3948 | 435 | return Py_False; | ||
3949 | 436 | default: // Should never happen | ||
3950 | 437 | return Py_NotImplemented; | ||
3951 | 438 | } | ||
3952 | 439 | } else { | ||
3953 | 440 | /* We don't special case this comparison, we just let python handle | ||
3954 | 441 | * it. | ||
3955 | 442 | */ | ||
3956 | 443 | Py_INCREF(Py_NotImplemented); | ||
3957 | 444 | return Py_NotImplemented; | ||
3958 | 445 | } | ||
3959 | 446 | /* Now we know that we have 2 StaticTuple objects, so let's compare them. | ||
3960 | 447 | * This code is inspired from tuplerichcompare, except we know our | ||
3961 | 448 | * objects are limited in scope, so we can inline some comparisons. | ||
3962 | 449 | */ | ||
3963 | 450 | if (v == w) { | ||
3964 | 451 | /* Identical pointers, we can shortcut this easily. */ | ||
3965 | 452 | switch (op) { | ||
3966 | 453 | case Py_EQ:case Py_LE:case Py_GE: | ||
3967 | 454 | Py_INCREF(Py_True); | ||
3968 | 455 | return Py_True; | ||
3969 | 456 | case Py_NE:case Py_LT:case Py_GT: | ||
3970 | 457 | Py_INCREF(Py_False); | ||
3971 | 458 | return Py_False; | ||
3972 | 459 | } | ||
3973 | 460 | } | ||
3974 | 461 | if (op == Py_EQ | ||
3975 | 462 | && _StaticTuple_is_interned(v_st) | ||
3976 | 463 | && _StaticTuple_is_interned(w_st)) | ||
3977 | 464 | { | ||
3978 | 465 | /* If both objects are interned, we know they are different if the | ||
3979 | 466 | * pointer is not the same, which would have been handled by the | ||
3980 | 467 | * previous if. No need to compare the entries. | ||
3981 | 468 | */ | ||
3982 | 469 | Py_INCREF(Py_False); | ||
3983 | 470 | return Py_False; | ||
3984 | 471 | } | ||
3985 | 472 | |||
3986 | 473 | /* The only time we are likely to compare items of different lengths is in | ||
3987 | 474 | * something like the interned_keys set. However, the hash is good enough | ||
3988 | 475 | * that it is rare. Note that 'tuple_richcompare' also does not compare | ||
3989 | 476 | * lengths here. | ||
3990 | 477 | */ | ||
3991 | 478 | vlen = v_st->size; | ||
3992 | 479 | wlen = w_st->size; | ||
3993 | 480 | min_len = (vlen < wlen) ? vlen : wlen; | ||
3994 | 481 | string_richcompare = PyString_Type.tp_richcompare; | ||
3995 | 482 | for (i = 0; i < min_len; i++) { | ||
3996 | 483 | PyObject *result = NULL; | ||
3997 | 484 | v_obj = StaticTuple_GET_ITEM(v_st, i); | ||
3998 | 485 | w_obj = StaticTuple_GET_ITEM(w_st, i); | ||
3999 | 486 | if (v_obj == w_obj) { | ||
4000 | 487 | /* Shortcut case, these must be identical */ | ||
4001 | 488 | continue; | ||
4002 | 489 | } | ||
4003 | 490 | if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj)) { | ||
4004 | 491 | result = string_richcompare(v_obj, w_obj, Py_EQ); | ||
4005 | 492 | } else if (StaticTuple_CheckExact(v_obj) && | ||
4006 | 493 | StaticTuple_CheckExact(w_obj)) | ||
4007 | 494 | { | ||
4008 | 495 | /* Both are StaticTuple types, so recurse */ | ||
4009 | 496 | result = StaticTuple_richcompare(v_obj, w_obj, Py_EQ); | ||
4010 | 497 | } else { | ||
4011 | 498 | /* Fall back to generic richcompare */ | ||
4012 | 499 | result = PyObject_RichCompare(v_obj, w_obj, Py_EQ); | ||
4013 | 500 | } | ||
4014 | 501 | if (result == NULL) { | ||
4015 | 502 | return NULL; /* There seems to be an error */ | ||
4016 | 503 | } | ||
4017 | 504 | if (result == Py_False) { | ||
4018 | 505 | // This entry is not identical, Shortcut for Py_EQ | ||
4019 | 506 | if (op == Py_EQ) { | ||
4020 | 507 | return result; | ||
4021 | 508 | } | ||
4022 | 509 | Py_DECREF(result); | ||
4023 | 510 | break; | ||
4024 | 511 | } | ||
4025 | 512 | if (result != Py_True) { | ||
4026 | 513 | /* We don't know *what* richcompare is returning, but it | ||
4027 | 514 | * isn't something we recognize | ||
4028 | 515 | */ | ||
4029 | 516 | PyErr_BadInternalCall(); | ||
4030 | 517 | Py_DECREF(result); | ||
4031 | 518 | return NULL; | ||
4032 | 519 | } | ||
4033 | 520 | Py_DECREF(result); | ||
4034 | 521 | } | ||
4035 | 522 | if (i >= min_len) { | ||
4036 | 523 | /* We walked off one of the lists, but everything compared equal so | ||
4037 | 524 | * far. Just compare the size. | ||
4038 | 525 | */ | ||
4039 | 526 | int cmp; | ||
4040 | 527 | PyObject *res; | ||
4041 | 528 | switch (op) { | ||
4042 | 529 | case Py_LT: cmp = vlen < wlen; break; | ||
4043 | 530 | case Py_LE: cmp = vlen <= wlen; break; | ||
4044 | 531 | case Py_EQ: cmp = vlen == wlen; break; | ||
4045 | 532 | case Py_NE: cmp = vlen != wlen; break; | ||
4046 | 533 | case Py_GT: cmp = vlen > wlen; break; | ||
4047 | 534 | case Py_GE: cmp = vlen >= wlen; break; | ||
4048 | 535 | default: return NULL; /* cannot happen */ | ||
4049 | 536 | } | ||
4050 | 537 | if (cmp) | ||
4051 | 538 | res = Py_True; | ||
4052 | 539 | else | ||
4053 | 540 | res = Py_False; | ||
4054 | 541 | Py_INCREF(res); | ||
4055 | 542 | return res; | ||
4056 | 543 | } | ||
4057 | 544 | /* The last item differs, shortcut the Py_NE case */ | ||
4058 | 545 | if (op == Py_NE) { | ||
4059 | 546 | Py_INCREF(Py_True); | ||
4060 | 547 | return Py_True; | ||
4061 | 548 | } | ||
4062 | 549 | /* It is some other comparison, go ahead and do the real check. */ | ||
4063 | 550 | if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj)) | ||
4064 | 551 | { | ||
4065 | 552 | return string_richcompare(v_obj, w_obj, op); | ||
4066 | 553 | } else if (StaticTuple_CheckExact(v_obj) && | ||
4067 | 554 | StaticTuple_CheckExact(w_obj)) | ||
4068 | 555 | { | ||
4069 | 556 | /* Both are StaticTuple types, so recurse */ | ||
4070 | 557 | return StaticTuple_richcompare(v_obj, w_obj, op); | ||
4071 | 558 | } else { | ||
4072 | 559 | return PyObject_RichCompare(v_obj, w_obj, op); | ||
4073 | 560 | } | ||
4074 | 561 | } | ||
4075 | 562 | |||
4076 | 563 | |||
4077 | 564 | static Py_ssize_t | ||
4078 | 565 | StaticTuple_length(StaticTuple *self) | ||
4079 | 566 | { | ||
4080 | 567 | return self->size; | ||
4081 | 568 | } | ||
4082 | 569 | |||
4083 | 570 | |||
4084 | 571 | static PyObject * | ||
4085 | 572 | StaticTuple__is_interned(StaticTuple *self) | ||
4086 | 573 | { | ||
4087 | 574 | if (_StaticTuple_is_interned(self)) { | ||
4088 | 575 | Py_INCREF(Py_True); | ||
4089 | 576 | return Py_True; | ||
4090 | 577 | } | ||
4091 | 578 | Py_INCREF(Py_False); | ||
4092 | 579 | return Py_False; | ||
4093 | 580 | } | ||
4094 | 581 | |||
4095 | 582 | static char StaticTuple__is_interned_doc[] = "_is_interned() => True/False\n" | ||
4096 | 583 | "Check to see if this tuple has been interned.\n"; | ||
4097 | 584 | |||
4098 | 585 | |||
4099 | 586 | static PyObject * | ||
4100 | 587 | StaticTuple_reduce(StaticTuple *self) | ||
4101 | 588 | { | ||
4102 | 589 | PyObject *result = NULL, *as_tuple = NULL; | ||
4103 | 590 | |||
4104 | 591 | result = PyTuple_New(2); | ||
4105 | 592 | if (!result) { | ||
4106 | 593 | return NULL; | ||
4107 | 594 | } | ||
4108 | 595 | as_tuple = StaticTuple_as_tuple(self); | ||
4109 | 596 | if (as_tuple == NULL) { | ||
4110 | 597 | Py_DECREF(result); | ||
4111 | 598 | return NULL; | ||
4112 | 599 | } | ||
4113 | 600 | Py_INCREF(&StaticTuple_Type); | ||
4114 | 601 | PyTuple_SET_ITEM(result, 0, (PyObject *)&StaticTuple_Type); | ||
4115 | 602 | PyTuple_SET_ITEM(result, 1, as_tuple); | ||
4116 | 603 | return result; | ||
4117 | 604 | } | ||
4118 | 605 | |||
4119 | 606 | static char StaticTuple_reduce_doc[] = "__reduce__() => tuple\n"; | ||
4120 | 607 | |||
4121 | 608 | |||
4122 | 609 | static PyObject * | ||
4123 | 610 | StaticTuple_add(PyObject *v, PyObject *w) | ||
4124 | 611 | { | ||
4125 | 612 | Py_ssize_t i, len_v, len_w; | ||
4126 | 613 | PyObject *item; | ||
4127 | 614 | StaticTuple *result; | ||
4128 | 615 | /* StaticTuples and plain tuples may be added (concatenated) to | ||
4129 | 616 | * StaticTuples. | ||
4130 | 617 | */ | ||
4131 | 618 | if (StaticTuple_CheckExact(v)) { | ||
4132 | 619 | len_v = ((StaticTuple*)v)->size; | ||
4133 | 620 | } else if (PyTuple_Check(v)) { | ||
4134 | 621 | len_v = PyTuple_GET_SIZE(v); | ||
4135 | 622 | } else { | ||
4136 | 623 | Py_INCREF(Py_NotImplemented); | ||
4137 | 624 | return Py_NotImplemented; | ||
4138 | 625 | } | ||
4139 | 626 | if (StaticTuple_CheckExact(w)) { | ||
4140 | 627 | len_w = ((StaticTuple*)w)->size; | ||
4141 | 628 | } else if (PyTuple_Check(w)) { | ||
4142 | 629 | len_w = PyTuple_GET_SIZE(w); | ||
4143 | 630 | } else { | ||
4144 | 631 | Py_INCREF(Py_NotImplemented); | ||
4145 | 632 | return Py_NotImplemented; | ||
4146 | 633 | } | ||
4147 | 634 | result = StaticTuple_New(len_v + len_w); | ||
4148 | 635 | if (result == NULL) | ||
4149 | 636 | return NULL; | ||
4150 | 637 | for (i = 0; i < len_v; ++i) { | ||
4151 | 638 | // This returns a new reference, which we then 'steal' with | ||
4152 | 639 | // StaticTuple_SET_ITEM | ||
4153 | 640 | item = PySequence_GetItem(v, i); | ||
4154 | 641 | if (item == NULL) { | ||
4155 | 642 | Py_DECREF(result); | ||
4156 | 643 | return NULL; | ||
4157 | 644 | } | ||
4158 | 645 | StaticTuple_SET_ITEM(result, i, item); | ||
4159 | 646 | } | ||
4160 | 647 | for (i = 0; i < len_w; ++i) { | ||
4161 | 648 | item = PySequence_GetItem(w, i); | ||
4162 | 649 | if (item == NULL) { | ||
4163 | 650 | Py_DECREF(result); | ||
4164 | 651 | return NULL; | ||
4165 | 652 | } | ||
4166 | 653 | StaticTuple_SET_ITEM(result, i+len_v, item); | ||
4167 | 654 | } | ||
4168 | 655 | if (!StaticTuple_check_items(result)) { | ||
4169 | 656 | Py_DECREF(result); | ||
4170 | 657 | return NULL; | ||
4171 | 658 | } | ||
4172 | 659 | return (PyObject *)result; | ||
4173 | 660 | } | ||
4174 | 661 | |||
4175 | 662 | static PyObject * | ||
4176 | 663 | StaticTuple_item(StaticTuple *self, Py_ssize_t offset) | ||
4177 | 664 | { | ||
4178 | 665 | PyObject *obj; | ||
4179 | 666 | /* We cast to (int) to avoid worrying about whether Py_ssize_t is a | ||
4180 | 667 | * long long, etc. offsets should never be >2**31 anyway. | ||
4181 | 668 | */ | ||
4182 | 669 | if (offset < 0) { | ||
4183 | 670 | PyErr_Format(PyExc_IndexError, "StaticTuple_item does not support" | ||
4184 | 671 | " negative indices: %d\n", (int)offset); | ||
4185 | 672 | } else if (offset >= self->size) { | ||
4186 | 673 | PyErr_Format(PyExc_IndexError, "StaticTuple index out of range" | ||
4187 | 674 | " %d >= %d", (int)offset, (int)self->size); | ||
4188 | 675 | return NULL; | ||
4189 | 676 | } | ||
4190 | 677 | obj = (PyObject *)self->items[offset]; | ||
4191 | 678 | Py_INCREF(obj); | ||
4192 | 679 | return obj; | ||
4193 | 680 | } | ||
4194 | 681 | |||
4195 | 682 | static PyObject * | ||
4196 | 683 | StaticTuple_slice(StaticTuple *self, Py_ssize_t ilow, Py_ssize_t ihigh) | ||
4197 | 684 | { | ||
4198 | 685 | PyObject *as_tuple, *result; | ||
4199 | 686 | |||
4200 | 687 | as_tuple = StaticTuple_as_tuple(self); | ||
4201 | 688 | if (as_tuple == NULL) { | ||
4202 | 689 | return NULL; | ||
4203 | 690 | } | ||
4204 | 691 | result = PyTuple_Type.tp_as_sequence->sq_slice(as_tuple, ilow, ihigh); | ||
4205 | 692 | Py_DECREF(as_tuple); | ||
4206 | 693 | return result; | ||
4207 | 694 | } | ||
4208 | 695 | |||
4209 | 696 | static int | ||
4210 | 697 | StaticTuple_traverse(StaticTuple *self, visitproc visit, void *arg) | ||
4211 | 698 | { | ||
4212 | 699 | Py_ssize_t i; | ||
4213 | 700 | for (i = self->size; --i >= 0;) { | ||
4214 | 701 | Py_VISIT(self->items[i]); | ||
4215 | 702 | } | ||
4216 | 703 | return 0; | ||
4217 | 704 | } | ||
4218 | 705 | |||
4219 | 706 | static char StaticTuple_doc[] = | ||
4220 | 707 | "C implementation of a StaticTuple structure." | ||
4221 | 708 | "\n This is used as StaticTuple(item1, item2, item3)" | ||
4222 | 709 | "\n This is similar to tuple, less flexible in what it" | ||
4223 | 710 | "\n supports, but also lighter memory consumption." | ||
4224 | 711 | "\n Note that the constructor mimics the () form of tuples" | ||
4225 | 712 | "\n Rather than the 'tuple()' constructor." | ||
4226 | 713 | "\n eg. StaticTuple(a, b) == (a, b) == tuple((a, b))"; | ||
4227 | 714 | |||
4228 | 715 | static PyMethodDef StaticTuple_methods[] = { | ||
4229 | 716 | {"as_tuple", (PyCFunction)StaticTuple_as_tuple, METH_NOARGS, StaticTuple_as_tuple_doc}, | ||
4230 | 717 | {"intern", (PyCFunction)StaticTuple_Intern, METH_NOARGS, StaticTuple_Intern_doc}, | ||
4231 | 718 | {"_is_interned", (PyCFunction)StaticTuple__is_interned, METH_NOARGS, | ||
4232 | 719 | StaticTuple__is_interned_doc}, | ||
4233 | 720 | {"from_sequence", (PyCFunction)StaticTuple_from_sequence, | ||
4234 | 721 | METH_STATIC | METH_VARARGS, | ||
4235 | 722 | "Create a StaticTuple from a given sequence. This functions" | ||
4236 | 723 | " the same as the tuple() constructor."}, | ||
4237 | 724 | {"__reduce__", (PyCFunction)StaticTuple_reduce, METH_NOARGS, StaticTuple_reduce_doc}, | ||
4238 | 725 | {NULL, NULL} /* sentinel */ | ||
4239 | 726 | }; | ||
4240 | 727 | |||
4241 | 728 | |||
4242 | 729 | static PyNumberMethods StaticTuple_as_number = { | ||
4243 | 730 | (binaryfunc) StaticTuple_add, /* nb_add */ | ||
4244 | 731 | 0, /* nb_subtract */ | ||
4245 | 732 | 0, /* nb_multiply */ | ||
4246 | 733 | 0, /* nb_divide */ | ||
4247 | 734 | 0, /* nb_remainder */ | ||
4248 | 735 | 0, /* nb_divmod */ | ||
4249 | 736 | 0, /* nb_power */ | ||
4250 | 737 | 0, /* nb_negative */ | ||
4251 | 738 | 0, /* nb_positive */ | ||
4252 | 739 | 0, /* nb_absolute */ | ||
4253 | 740 | 0, /* nb_nonzero */ | ||
4254 | 741 | 0, /* nb_invert */ | ||
4255 | 742 | 0, /* nb_lshift */ | ||
4256 | 743 | 0, /* nb_rshift */ | ||
4257 | 744 | 0, /* nb_and */ | ||
4258 | 745 | 0, /* nb_xor */ | ||
4259 | 746 | 0, /* nb_or */ | ||
4260 | 747 | 0, /* nb_coerce */ | ||
4261 | 748 | }; | ||
4262 | 749 | |||
4263 | 750 | |||
4264 | 751 | static PySequenceMethods StaticTuple_as_sequence = { | ||
4265 | 752 | (lenfunc)StaticTuple_length, /* sq_length */ | ||
4266 | 753 | 0, /* sq_concat */ | ||
4267 | 754 | 0, /* sq_repeat */ | ||
4268 | 755 | (ssizeargfunc)StaticTuple_item, /* sq_item */ | ||
4269 | 756 | (ssizessizeargfunc)StaticTuple_slice, /* sq_slice */ | ||
4270 | 757 | 0, /* sq_ass_item */ | ||
4271 | 758 | 0, /* sq_ass_slice */ | ||
4272 | 759 | 0, /* sq_contains */ | ||
4273 | 760 | }; | ||
4274 | 761 | |||
4275 | 762 | /* TODO: Implement StaticTuple_as_mapping. | ||
4276 | 763 | * The only thing we really want to support from there is mp_subscript, | ||
4277 | 764 | * so that we could support extended slicing (foo[::2]). Not worth it | ||
4278 | 765 | * yet, though. | ||
4279 | 766 | */ | ||
4280 | 767 | |||
4281 | 768 | |||
4282 | 769 | PyTypeObject StaticTuple_Type = { | ||
4283 | 770 | PyObject_HEAD_INIT(NULL) | ||
4284 | 771 | 0, /* ob_size */ | ||
4285 | 772 | "bzrlib._static_tuple_c.StaticTuple", /* tp_name */ | ||
4286 | 773 | sizeof(StaticTuple), /* tp_basicsize */ | ||
4287 | 774 | sizeof(PyObject *), /* tp_itemsize */ | ||
4288 | 775 | (destructor)StaticTuple_dealloc, /* tp_dealloc */ | ||
4289 | 776 | 0, /* tp_print */ | ||
4290 | 777 | 0, /* tp_getattr */ | ||
4291 | 778 | 0, /* tp_setattr */ | ||
4292 | 779 | 0, /* tp_compare */ | ||
4293 | 780 | (reprfunc)StaticTuple_repr, /* tp_repr */ | ||
4294 | 781 | &StaticTuple_as_number, /* tp_as_number */ | ||
4295 | 782 | &StaticTuple_as_sequence, /* tp_as_sequence */ | ||
4296 | 783 | 0, /* tp_as_mapping */ | ||
4297 | 784 | (hashfunc)StaticTuple_hash, /* tp_hash */ | ||
4298 | 785 | 0, /* tp_call */ | ||
4299 | 786 | 0, /* tp_str */ | ||
4300 | 787 | 0, /* tp_getattro */ | ||
4301 | 788 | 0, /* tp_setattro */ | ||
4302 | 789 | 0, /* tp_as_buffer */ | ||
4303 | 790 | /* Py_TPFLAGS_CHECKTYPES tells the number operations that they shouldn't | ||
4304 | 791 | * try to 'coerce' but instead stuff like 'add' will check it arguments. | ||
4305 | 792 | */ | ||
4306 | 793 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags*/ | ||
4307 | 794 | StaticTuple_doc, /* tp_doc */ | ||
4308 | 795 | /* gc.get_referents checks the IS_GC flag before it calls tp_traverse | ||
4309 | 796 | * And we don't include this object in the garbage collector because we | ||
4310 | 797 | * know it doesn't create cycles. However, 'meliae' will follow | ||
4311 | 798 | * tp_traverse, even if the object isn't GC, and we want that. | ||
4312 | 799 | */ | ||
4313 | 800 | (traverseproc)StaticTuple_traverse, /* tp_traverse */ | ||
4314 | 801 | 0, /* tp_clear */ | ||
4315 | 802 | StaticTuple_richcompare, /* tp_richcompare */ | ||
4316 | 803 | 0, /* tp_weaklistoffset */ | ||
4317 | 804 | // without implementing tp_iter, Python will fall back to PySequence* | ||
4318 | 805 | // which seems to work ok, we may need something faster/lighter in the | ||
4319 | 806 | // future. | ||
4320 | 807 | 0, /* tp_iter */ | ||
4321 | 808 | 0, /* tp_iternext */ | ||
4322 | 809 | StaticTuple_methods, /* tp_methods */ | ||
4323 | 810 | 0, /* tp_members */ | ||
4324 | 811 | 0, /* tp_getset */ | ||
4325 | 812 | 0, /* tp_base */ | ||
4326 | 813 | 0, /* tp_dict */ | ||
4327 | 814 | 0, /* tp_descr_get */ | ||
4328 | 815 | 0, /* tp_descr_set */ | ||
4329 | 816 | 0, /* tp_dictoffset */ | ||
4330 | 817 | 0, /* tp_init */ | ||
4331 | 818 | 0, /* tp_alloc */ | ||
4332 | 819 | StaticTuple_new_constructor, /* tp_new */ | ||
4333 | 820 | }; | ||
4334 | 821 | |||
4335 | 822 | |||
4336 | 823 | static PyMethodDef static_tuple_c_methods[] = { | ||
4337 | 824 | {NULL, NULL} | ||
4338 | 825 | }; | ||
4339 | 826 | |||
4340 | 827 | |||
4341 | 828 | static void | ||
4342 | 829 | setup_interned_tuples(PyObject *m) | ||
4343 | 830 | { | ||
4344 | 831 | _interned_tuples = (PyObject *)SimpleSet_New(); | ||
4345 | 832 | if (_interned_tuples != NULL) { | ||
4346 | 833 | Py_INCREF(_interned_tuples); | ||
4347 | 834 | PyModule_AddObject(m, "_interned_tuples", _interned_tuples); | ||
4348 | 835 | } | ||
4349 | 836 | } | ||
4350 | 837 | |||
4351 | 838 | |||
4352 | 839 | static void | ||
4353 | 840 | setup_empty_tuple(PyObject *m) | ||
4354 | 841 | { | ||
4355 | 842 | StaticTuple *stuple; | ||
4356 | 843 | if (_interned_tuples == NULL) { | ||
4357 | 844 | fprintf(stderr, "You need to call setup_interned_tuples() before" | ||
4358 | 845 | " setup_empty_tuple, because we intern it.\n"); | ||
4359 | 846 | } | ||
4360 | 847 | // We need to create the empty tuple | ||
4361 | 848 | stuple = (StaticTuple *)StaticTuple_New(0); | ||
4362 | 849 | _empty_tuple = StaticTuple_Intern(stuple); | ||
4363 | 850 | assert(_empty_tuple == stuple); | ||
4364 | 851 | // At this point, refcnt is 2: 1 from New(), and 1 from the return from | ||
4365 | 852 | // intern(). We will keep 1 for the _empty_tuple global, and use the other | ||
4366 | 853 | // for the module reference. | ||
4367 | 854 | PyModule_AddObject(m, "_empty_tuple", (PyObject *)_empty_tuple); | ||
4368 | 855 | } | ||
4369 | 856 | |||
4370 | 857 | static int | ||
4371 | 858 | _StaticTuple_CheckExact(PyObject *obj) | ||
4372 | 859 | { | ||
4373 | 860 | return StaticTuple_CheckExact(obj); | ||
4374 | 861 | } | ||
4375 | 862 | |||
4376 | 863 | static void | ||
4377 | 864 | setup_c_api(PyObject *m) | ||
4378 | 865 | { | ||
4379 | 866 | _export_function(m, "StaticTuple_New", StaticTuple_New, | ||
4380 | 867 | "StaticTuple *(Py_ssize_t)"); | ||
4381 | 868 | _export_function(m, "StaticTuple_Intern", StaticTuple_Intern, | ||
4382 | 869 | "StaticTuple *(StaticTuple *)"); | ||
4383 | 870 | _export_function(m, "StaticTuple_FromSequence", StaticTuple_FromSequence, | ||
4384 | 871 | "StaticTuple *(PyObject *)"); | ||
4385 | 872 | _export_function(m, "_StaticTuple_CheckExact", _StaticTuple_CheckExact, | ||
4386 | 873 | "int(PyObject *)"); | ||
4387 | 874 | } | ||
4388 | 875 | |||
4389 | 876 | |||
4390 | 877 | static int | ||
4391 | 878 | _workaround_pyrex_096(void) | ||
4392 | 879 | { | ||
4393 | 880 | /* Work around an incompatibility in how pyrex 0.9.6 exports a module, | ||
4394 | 881 | * versus how pyrex 0.9.8 and cython 0.11 export it. | ||
4395 | 882 | * Namely 0.9.6 exports import__simple_set_pyx and tries to | ||
4396 | 883 | * "import _simple_set_pyx" but it is available only as | ||
4397 | 884 | * "import bzrlib._simple_set_pyx" | ||
4398 | 885 | * It is a shame to hack up sys.modules, but that is what we've got to do. | ||
4399 | 886 | */ | ||
4400 | 887 | PyObject *sys_module = NULL, *modules = NULL, *set_module = NULL; | ||
4401 | 888 | int retval = -1; | ||
4402 | 889 | |||
4403 | 890 | /* Clear out the current ImportError exception, and try again. */ | ||
4404 | 891 | PyErr_Clear(); | ||
4405 | 892 | /* Note that this only seems to work if somewhere else imports | ||
4406 | 893 | * bzrlib._simple_set_pyx before importing bzrlib._static_tuple_c | ||
4407 | 894 | */ | ||
4408 | 895 | set_module = PyImport_ImportModule("bzrlib._simple_set_pyx"); | ||
4409 | 896 | if (set_module == NULL) { | ||
4410 | 897 | goto end; | ||
4411 | 898 | } | ||
4412 | 899 | /* Add the _simple_set_pyx into sys.modules at the appropriate location. */ | ||
4413 | 900 | sys_module = PyImport_ImportModule("sys"); | ||
4414 | 901 | if (sys_module == NULL) { | ||
4415 | 902 | goto end; | ||
4416 | 903 | } | ||
4417 | 904 | modules = PyObject_GetAttrString(sys_module, "modules"); | ||
4418 | 905 | if (modules == NULL || !PyDict_Check(modules)) { | ||
4419 | 906 | goto end; | ||
4420 | 907 | } | ||
4421 | 908 | PyDict_SetItemString(modules, "_simple_set_pyx", set_module); | ||
4422 | 909 | /* Now that we have hacked it in, try the import again. */ | ||
4423 | 910 | retval = import_bzrlib___simple_set_pyx(); | ||
4424 | 911 | end: | ||
4425 | 912 | Py_XDECREF(set_module); | ||
4426 | 913 | Py_XDECREF(sys_module); | ||
4427 | 914 | Py_XDECREF(modules); | ||
4428 | 915 | return retval; | ||
4429 | 916 | } | ||
4430 | 917 | |||
4431 | 918 | |||
4432 | 919 | PyMODINIT_FUNC | ||
4433 | 920 | init_static_tuple_c(void) | ||
4434 | 921 | { | ||
4435 | 922 | PyObject* m; | ||
4436 | 923 | |||
4437 | 924 | StaticTuple_Type.tp_getattro = PyObject_GenericGetAttr; | ||
4438 | 925 | if (PyType_Ready(&StaticTuple_Type) < 0) | ||
4439 | 926 | return; | ||
4440 | 927 | |||
4441 | 928 | m = Py_InitModule3("_static_tuple_c", static_tuple_c_methods, | ||
4442 | 929 | "C implementation of a StaticTuple structure"); | ||
4443 | 930 | if (m == NULL) | ||
4444 | 931 | return; | ||
4445 | 932 | |||
4446 | 933 | Py_INCREF(&StaticTuple_Type); | ||
4447 | 934 | PyModule_AddObject(m, "StaticTuple", (PyObject *)&StaticTuple_Type); | ||
4448 | 935 | if (import_bzrlib___simple_set_pyx() == -1 | ||
4449 | 936 | && _workaround_pyrex_096() == -1) | ||
4450 | 937 | { | ||
4451 | 938 | return; | ||
4452 | 939 | } | ||
4453 | 940 | setup_interned_tuples(m); | ||
4454 | 941 | setup_empty_tuple(m); | ||
4455 | 942 | setup_c_api(m); | ||
4456 | 943 | } | ||
4457 | 944 | |||
4458 | 945 | // vim: tabstop=4 sw=4 expandtab | ||
4459 | 0 | 946 | ||
4460 | === added file 'bzrlib/_static_tuple_c.h' | |||
4461 | --- bzrlib/_static_tuple_c.h 1970-01-01 00:00:00 +0000 | |||
4462 | +++ bzrlib/_static_tuple_c.h 2010-02-18 00:00:51 +0000 | |||
4463 | @@ -0,0 +1,116 @@ | |||
4464 | 1 | /* Copyright (C) 2009 Canonical Ltd | ||
4465 | 2 | * | ||
4466 | 3 | * This program is free software; you can redistribute it and/or modify | ||
4467 | 4 | * it under the terms of the GNU General Public License as published by | ||
4468 | 5 | * the Free Software Foundation; either version 2 of the License, or | ||
4469 | 6 | * (at your option) any later version. | ||
4470 | 7 | * | ||
4471 | 8 | * This program is distributed in the hope that it will be useful, | ||
4472 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4473 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4474 | 11 | * GNU General Public License for more details. | ||
4475 | 12 | * | ||
4476 | 13 | * You should have received a copy of the GNU General Public License | ||
4477 | 14 | * along with this program; if not, write to the Free Software | ||
4478 | 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
4479 | 16 | */ | ||
4480 | 17 | |||
4481 | 18 | #ifndef _STATIC_TUPLE_H_ | ||
4482 | 19 | #define _STATIC_TUPLE_H_ | ||
4483 | 20 | #include <Python.h> | ||
4484 | 21 | #include <string.h> | ||
4485 | 22 | |||
4486 | 23 | #define STATIC_TUPLE_HAS_HASH 0 | ||
4487 | 24 | /* Caching the hash adds memory, but allows us to save a little time during | ||
4488 | 25 | * lookups. TIMEIT hash(key) shows it as | ||
4489 | 26 | * 0.108usec w/ hash | ||
4490 | 27 | * 0.160usec w/o hash | ||
4491 | 28 | * Note that the entries themselves are strings, which already cache their | ||
4492 | 29 | * hashes. So while there is a 1.5:1 difference in the time for hash(), it is | ||
4493 | 30 | * already a function which is quite fast. Probably the only reason we might | ||
4494 | 31 | * want to do so, is if we customized SimpleSet to the point that the item | ||
4495 | 32 | * pointers were exactly certain types, and then accessed table[i]->hash | ||
4496 | 33 | * directly. So far StaticTuple_hash() is fast enough to not warrant the memory | ||
4497 | 34 | * difference. | ||
4498 | 35 | */ | ||
4499 | 36 | |||
4500 | 37 | /* This defines a single variable-width key. | ||
4501 | 38 | * It is basically the same as a tuple, but | ||
4502 | 39 | * 1) Lighter weight in memory | ||
4503 | 40 | * 2) Only supports strings or other static types (that don't reference other | ||
4504 | 41 | * objects.) | ||
4505 | 42 | */ | ||
4506 | 43 | |||
4507 | 44 | #define STATIC_TUPLE_INTERNED_FLAG 0x01 | ||
4508 | 45 | typedef struct { | ||
4509 | 46 | PyObject_HEAD | ||
4510 | 47 | // We could go with unsigned short here, and support 64k width tuples | ||
4511 | 48 | // without any memory impact, might be worthwhile | ||
4512 | 49 | unsigned char size; | ||
4513 | 50 | unsigned char flags; | ||
4514 | 51 | unsigned char _unused0; | ||
4515 | 52 | unsigned char _unused1; | ||
4516 | 53 | // Note that on 64-bit, we actually have 4-more unused bytes | ||
4517 | 54 | // because items will always be aligned to a 64-bit boundary | ||
4518 | 55 | #if STATIC_TUPLE_HAS_HASH | ||
4519 | 56 | long hash; | ||
4520 | 57 | #endif | ||
4521 | 58 | PyObject *items[0]; | ||
4522 | 59 | } StaticTuple; | ||
4523 | 60 | extern PyTypeObject StaticTuple_Type; | ||
4524 | 61 | |||
4525 | 62 | typedef struct { | ||
4526 | 63 | PyObject_VAR_HEAD | ||
4527 | 64 | PyObject *table[0]; | ||
4528 | 65 | } KeyIntern; | ||
4529 | 66 | |||
4530 | 67 | #define StaticTuple_SET_ITEM(key, offset, val) \ | ||
4531 | 68 | ((((StaticTuple*)(key))->items[(offset)]) = ((PyObject *)(val))) | ||
4532 | 69 | #define StaticTuple_GET_ITEM(key, offset) (((StaticTuple*)key)->items[offset]) | ||
4533 | 70 | |||
4534 | 71 | |||
4535 | 72 | #ifdef STATIC_TUPLE_MODULE | ||
4536 | 73 | /* Used when compiling _static_tuple_c.c */ | ||
4537 | 74 | |||
4538 | 75 | static StaticTuple * StaticTuple_New(Py_ssize_t); | ||
4539 | 76 | static StaticTuple * StaticTuple_Intern(StaticTuple *self); | ||
4540 | 77 | static StaticTuple * StaticTuple_FromSequence(PyObject *); | ||
4541 | 78 | #define StaticTuple_CheckExact(op) (Py_TYPE(op) == &StaticTuple_Type) | ||
4542 | 79 | |||
4543 | 80 | #else | ||
4544 | 81 | /* Used as the foreign api */ | ||
4545 | 82 | |||
4546 | 83 | #include "_import_c_api.h" | ||
4547 | 84 | |||
4548 | 85 | static StaticTuple *(*StaticTuple_New)(Py_ssize_t); | ||
4549 | 86 | static StaticTuple *(*StaticTuple_Intern)(StaticTuple *); | ||
4550 | 87 | static StaticTuple *(*StaticTuple_FromSequence)(PyObject *); | ||
4551 | 88 | static PyTypeObject *_p_StaticTuple_Type; | ||
4552 | 89 | |||
4553 | 90 | #define StaticTuple_CheckExact(op) (Py_TYPE(op) == _p_StaticTuple_Type) | ||
4554 | 91 | static int (*_StaticTuple_CheckExact)(PyObject *); | ||
4555 | 92 | |||
4556 | 93 | |||
4557 | 94 | /* Return -1 and set exception on error, 0 on success */ | ||
4558 | 95 | static int | ||
4559 | 96 | import_static_tuple_c(void) | ||
4560 | 97 | { | ||
4561 | 98 | struct function_description functions[] = { | ||
4562 | 99 | {"StaticTuple_New", (void **)&StaticTuple_New, | ||
4563 | 100 | "StaticTuple *(Py_ssize_t)"}, | ||
4564 | 101 | {"StaticTuple_Intern", (void **)&StaticTuple_Intern, | ||
4565 | 102 | "StaticTuple *(StaticTuple *)"}, | ||
4566 | 103 | {"StaticTuple_FromSequence", (void **)&StaticTuple_FromSequence, | ||
4567 | 104 | "StaticTuple *(PyObject *)"}, | ||
4568 | 105 | {"_StaticTuple_CheckExact", (void **)&_StaticTuple_CheckExact, | ||
4569 | 106 | "int(PyObject *)"}, | ||
4570 | 107 | {NULL}}; | ||
4571 | 108 | struct type_description types[] = { | ||
4572 | 109 | {"StaticTuple", &_p_StaticTuple_Type}, | ||
4573 | 110 | {NULL}}; | ||
4574 | 111 | return _import_extension_module("bzrlib._static_tuple_c", | ||
4575 | 112 | functions, types); | ||
4576 | 113 | } | ||
4577 | 114 | |||
4578 | 115 | #endif // !STATIC_TUPLE_MODULE | ||
4579 | 116 | #endif // !_STATIC_TUPLE_H_ | ||
4580 | 0 | 117 | ||
4581 | === added file 'bzrlib/_static_tuple_c.pxd' | |||
4582 | --- bzrlib/_static_tuple_c.pxd 1970-01-01 00:00:00 +0000 | |||
4583 | +++ bzrlib/_static_tuple_c.pxd 2010-02-18 00:00:51 +0000 | |||
4584 | @@ -0,0 +1,44 @@ | |||
4585 | 1 | # Copyright (C) 2009, 2010 Canonical Ltd | ||
4586 | 2 | # | ||
4587 | 3 | # This program is free software; you can redistribute it and/or modify | ||
4588 | 4 | # it under the terms of the GNU General Public License as published by | ||
4589 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
4590 | 6 | # (at your option) any later version. | ||
4591 | 7 | # | ||
4592 | 8 | # This program is distributed in the hope that it will be useful, | ||
4593 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4594 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4595 | 11 | # GNU General Public License for more details. | ||
4596 | 12 | # | ||
4597 | 13 | # You should have received a copy of the GNU General Public License | ||
4598 | 14 | # along with this program; if not, write to the Free Software | ||
4599 | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
4600 | 16 | |||
4601 | 17 | """The interface definition file for the StaticTuple class.""" | ||
4602 | 18 | |||
4603 | 19 | |||
4604 | 20 | cdef extern from "Python.h": | ||
4605 | 21 | ctypedef int Py_ssize_t # Required for older pyrex versions | ||
4606 | 22 | ctypedef struct PyObject: | ||
4607 | 23 | pass | ||
4608 | 24 | |||
4609 | 25 | cdef extern from "_static_tuple_c.h": | ||
4610 | 26 | ctypedef class bzrlib._static_tuple_c.StaticTuple [object StaticTuple]: | ||
4611 | 27 | cdef unsigned char size | ||
4612 | 28 | cdef unsigned char flags | ||
4613 | 29 | cdef PyObject *items[0] | ||
4614 | 30 | |||
4615 | 31 | # Must be called before using any of the C api, as it sets the function | ||
4616 | 32 | # pointers in memory. | ||
4617 | 33 | int import_static_tuple_c() except -1 | ||
4618 | 34 | StaticTuple StaticTuple_New(Py_ssize_t) | ||
4619 | 35 | StaticTuple StaticTuple_Intern(StaticTuple) | ||
4620 | 36 | |||
4621 | 37 | # Steals a reference and val must be a valid type, no checking is done | ||
4622 | 38 | void StaticTuple_SET_ITEM(StaticTuple key, Py_ssize_t offset, object val) | ||
4623 | 39 | # We would normally use PyObject * here. However it seems that cython/pyrex | ||
4624 | 40 | # treat the PyObject defined in this header as something different than one | ||
4625 | 41 | # defined in a .pyx file. And since we don't INCREF, we need a raw pointer, | ||
4626 | 42 | # not an 'object' return value. | ||
4627 | 43 | void *StaticTuple_GET_ITEM(StaticTuple key, Py_ssize_t offset) | ||
4628 | 44 | int StaticTuple_CheckExact(object) | ||
4629 | 0 | 45 | ||
4630 | === added file 'bzrlib/_static_tuple_py.py' | |||
4631 | --- bzrlib/_static_tuple_py.py 1970-01-01 00:00:00 +0000 | |||
4632 | +++ bzrlib/_static_tuple_py.py 2010-02-18 00:00:52 +0000 | |||
4633 | @@ -0,0 +1,80 @@ | |||
4634 | 1 | # Copyright (C) 2009, 2010 Canonical Ltd | ||
4635 | 2 | # | ||
4636 | 3 | # This program is free software; you can redistribute it and/or modify | ||
4637 | 4 | # it under the terms of the GNU General Public License as published by | ||
4638 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
4639 | 6 | # (at your option) any later version. | ||
4640 | 7 | # | ||
4641 | 8 | # This program is distributed in the hope that it will be useful, | ||
4642 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4643 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4644 | 11 | # GNU General Public License for more details. | ||
4645 | 12 | # | ||
4646 | 13 | # You should have received a copy of the GNU General Public License | ||
4647 | 14 | # along with this program; if not, write to the Free Software | ||
4648 | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
4649 | 16 | |||
4650 | 17 | """The pure-python implementation of the StaticTuple type. | ||
4651 | 18 | |||
4652 | 19 | Note that it is generally just implemented as using tuples of tuples of | ||
4653 | 20 | strings. | ||
4654 | 21 | """ | ||
4655 | 22 | |||
4656 | 23 | |||
4657 | 24 | class StaticTuple(tuple): | ||
4658 | 25 | """A static type, similar to a tuple of strings.""" | ||
4659 | 26 | |||
4660 | 27 | __slots__ = () | ||
4661 | 28 | |||
4662 | 29 | def __new__(cls, *args): | ||
4663 | 30 | # Make the empty StaticTuple a singleton | ||
4664 | 31 | if not args and _empty_tuple is not None: | ||
4665 | 32 | return _empty_tuple | ||
4666 | 33 | return tuple.__new__(cls, args) | ||
4667 | 34 | |||
4668 | 35 | def __init__(self, *args): | ||
4669 | 36 | """Create a new 'StaticTuple'""" | ||
4670 | 37 | num_keys = len(args) | ||
4671 | 38 | if num_keys < 0 or num_keys > 255: | ||
4672 | 39 | raise TypeError('StaticTuple(...) takes from 0 to 255 items') | ||
4673 | 40 | for bit in args: | ||
4674 | 41 | if type(bit) not in (str, StaticTuple, unicode, int, long, float, | ||
4675 | 42 | None.__class__, bool): | ||
4676 | 43 | raise TypeError('StaticTuple can only point to' | ||
4677 | 44 | ' StaticTuple, str, unicode, int, long, float, bool, or' | ||
4678 | 45 | ' None not %s' % (type(bit),)) | ||
4679 | 46 | # We don't need to pass args to tuple.__init__, because that was | ||
4680 | 47 | # already handled in __new__. | ||
4681 | 48 | tuple.__init__(self) | ||
4682 | 49 | |||
4683 | 50 | def __repr__(self): | ||
4684 | 51 | return '%s%s' % (self.__class__.__name__, tuple.__repr__(self)) | ||
4685 | 52 | |||
4686 | 53 | def __reduce__(self): | ||
4687 | 54 | return (StaticTuple, tuple(self)) | ||
4688 | 55 | |||
4689 | 56 | def __add__(self, other): | ||
4690 | 57 | """Concatenate self with other""" | ||
4691 | 58 | return StaticTuple.from_sequence(tuple.__add__(self,other)) | ||
4692 | 59 | |||
4693 | 60 | def as_tuple(self): | ||
4694 | 61 | return tuple(self) | ||
4695 | 62 | |||
4696 | 63 | def intern(self): | ||
4697 | 64 | return _interned_tuples.setdefault(self, self) | ||
4698 | 65 | |||
4699 | 66 | @staticmethod | ||
4700 | 67 | def from_sequence(seq): | ||
4701 | 68 | """Convert a sequence object into a StaticTuple instance.""" | ||
4702 | 69 | if isinstance(seq, StaticTuple): | ||
4703 | 70 | # it already is | ||
4704 | 71 | return seq | ||
4705 | 72 | return StaticTuple(*seq) | ||
4706 | 73 | |||
4707 | 74 | |||
4708 | 75 | |||
4709 | 76 | # Have to set it to None first, so that __new__ can determine whether | ||
4710 | 77 | # the _empty_tuple singleton has been created yet or not. | ||
4711 | 78 | _empty_tuple = None | ||
4712 | 79 | _empty_tuple = StaticTuple() | ||
4713 | 80 | _interned_tuples = {} | ||
4714 | 0 | 81 | ||
4715 | === modified file 'bzrlib/_walkdirs_win32.pyx' | |||
4716 | --- bzrlib/_walkdirs_win32.pyx 2010-01-05 04:59:57 +0000 | |||
4717 | +++ bzrlib/_walkdirs_win32.pyx 2010-02-18 00:00:51 +0000 | |||
4718 | @@ -1,4 +1,4 @@ | |||
4720 | 1 | # Copyright (C) 2008 Canonical Ltd | 1 | # Copyright (C) 2008, 2009, 2010 Canonical Ltd |
4721 | 2 | # | 2 | # |
4722 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
4723 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
4724 | 5 | 5 | ||
4725 | === modified file 'bzrlib/annotate.py' | |||
4726 | --- bzrlib/annotate.py 2009-08-17 18:52:01 +0000 | |||
4727 | +++ bzrlib/annotate.py 2010-02-18 00:00:51 +0000 | |||
4728 | @@ -458,5 +458,6 @@ | |||
4729 | 458 | 458 | ||
4730 | 459 | try: | 459 | try: |
4731 | 460 | from bzrlib._annotator_pyx import Annotator | 460 | from bzrlib._annotator_pyx import Annotator |
4733 | 461 | except ImportError: | 461 | except ImportError, e: |
4734 | 462 | osutils.failed_to_load_extension(e) | ||
4735 | 462 | from bzrlib._annotator_py import Annotator | 463 | from bzrlib._annotator_py import Annotator |
4736 | 463 | 464 | ||
4737 | === modified file 'bzrlib/benchmarks/bench_dirstate.py' | |||
4738 | --- bzrlib/benchmarks/bench_dirstate.py 2009-06-22 15:39:42 +0000 | |||
4739 | +++ bzrlib/benchmarks/bench_dirstate.py 2010-02-18 00:00:52 +0000 | |||
4740 | @@ -1,4 +1,4 @@ | |||
4742 | 1 | # Copyright (C) 2007 Canonical Ltd | 1 | # Copyright (C) 2007, 2009, 2010 Canonical Ltd |
4743 | 2 | # | 2 | # |
4744 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
4745 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
4746 | @@ -26,7 +26,7 @@ | |||
4747 | 26 | tests, | 26 | tests, |
4748 | 27 | ) | 27 | ) |
4749 | 28 | from bzrlib.tests.test__dirstate_helpers import ( | 28 | from bzrlib.tests.test__dirstate_helpers import ( |
4751 | 29 | CompiledDirstateHelpersFeature, | 29 | compiled_dirstate_helpers_feature, |
4752 | 30 | ) | 30 | ) |
4753 | 31 | 31 | ||
4754 | 32 | 32 | ||
4755 | @@ -206,7 +206,7 @@ | |||
4756 | 206 | state.unlock() | 206 | state.unlock() |
4757 | 207 | 207 | ||
4758 | 208 | def test__read_dirblocks_20k_tree_0_parents_pyx(self): | 208 | def test__read_dirblocks_20k_tree_0_parents_pyx(self): |
4760 | 209 | self.requireFeature(CompiledDirstateHelpersFeature) | 209 | self.requireFeature(compiled_dirstate_helpers_feature) |
4761 | 210 | from bzrlib._dirstate_helpers_pyx import _read_dirblocks | 210 | from bzrlib._dirstate_helpers_pyx import _read_dirblocks |
4762 | 211 | state = self.build_20k_dirstate() | 211 | state = self.build_20k_dirstate() |
4763 | 212 | state.lock_read() | 212 | state.lock_read() |
4764 | @@ -231,7 +231,7 @@ | |||
4765 | 231 | state.unlock() | 231 | state.unlock() |
4766 | 232 | 232 | ||
4767 | 233 | def test__read_dirblocks_20k_tree_1_parent_pyx(self): | 233 | def test__read_dirblocks_20k_tree_1_parent_pyx(self): |
4769 | 234 | self.requireFeature(CompiledDirstateHelpersFeature) | 234 | self.requireFeature(compiled_dirstate_helpers_feature) |
4770 | 235 | from bzrlib._dirstate_helpers_pyx import _read_dirblocks | 235 | from bzrlib._dirstate_helpers_pyx import _read_dirblocks |
4771 | 236 | state = self.build_20k_dirstate_with_parents(1) | 236 | state = self.build_20k_dirstate_with_parents(1) |
4772 | 237 | state.lock_read() | 237 | state.lock_read() |
4773 | @@ -256,7 +256,7 @@ | |||
4774 | 256 | state.unlock() | 256 | state.unlock() |
4775 | 257 | 257 | ||
4776 | 258 | def test__read_dirblocks_20k_tree_2_parents_pyx(self): | 258 | def test__read_dirblocks_20k_tree_2_parents_pyx(self): |
4778 | 259 | self.requireFeature(CompiledDirstateHelpersFeature) | 259 | self.requireFeature(compiled_dirstate_helpers_feature) |
4779 | 260 | from bzrlib._dirstate_helpers_pyx import _read_dirblocks | 260 | from bzrlib._dirstate_helpers_pyx import _read_dirblocks |
4780 | 261 | state = self.build_20k_dirstate_with_parents(2) | 261 | state = self.build_20k_dirstate_with_parents(2) |
4781 | 262 | state.lock_read() | 262 | state.lock_read() |
4782 | @@ -337,7 +337,7 @@ | |||
4783 | 337 | state.unlock() | 337 | state.unlock() |
4784 | 338 | 338 | ||
4785 | 339 | def test_bisect_dirblock_pyx(self): | 339 | def test_bisect_dirblock_pyx(self): |
4787 | 340 | self.requireFeature(CompiledDirstateHelpersFeature) | 340 | self.requireFeature(compiled_dirstate_helpers_feature) |
4788 | 341 | from bzrlib._dirstate_helpers_pyx import bisect_dirblock | 341 | from bzrlib._dirstate_helpers_pyx import bisect_dirblock |
4789 | 342 | state = self.build_10k_dirstate_dirs() | 342 | state = self.build_10k_dirstate_dirs() |
4790 | 343 | state.lock_read() | 343 | state.lock_read() |
4791 | @@ -420,7 +420,7 @@ | |||
4792 | 420 | [(3, 1), (3, 1), (3, 1), (3, 2)]) | 420 | [(3, 1), (3, 1), (3, 1), (3, 2)]) |
4793 | 421 | 421 | ||
4794 | 422 | def test_cmp_by_dirs_pyrex(self): | 422 | def test_cmp_by_dirs_pyrex(self): |
4796 | 423 | self.requireFeature(CompiledDirstateHelpersFeature) | 423 | self.requireFeature(compiled_dirstate_helpers_feature) |
4797 | 424 | from bzrlib._dirstate_helpers_pyx import cmp_by_dirs | 424 | from bzrlib._dirstate_helpers_pyx import cmp_by_dirs |
4798 | 425 | self.compareAllPaths(cmp_by_dirs, | 425 | self.compareAllPaths(cmp_by_dirs, |
4799 | 426 | [(3, 1), (3, 1), (3, 1), (3, 2)]) | 426 | [(3, 1), (3, 1), (3, 1), (3, 2)]) |
4800 | 427 | 427 | ||
4801 | === modified file 'bzrlib/bencode.py' | |||
4802 | --- bzrlib/bencode.py 2009-06-03 14:14:31 +0000 | |||
4803 | +++ bzrlib/bencode.py 2010-02-18 00:00:52 +0000 | |||
4804 | @@ -16,7 +16,10 @@ | |||
4805 | 16 | 16 | ||
4806 | 17 | """Wrapper around the bencode pyrex and python implementation""" | 17 | """Wrapper around the bencode pyrex and python implementation""" |
4807 | 18 | 18 | ||
4808 | 19 | from bzrlib import osutils | ||
4809 | 20 | |||
4810 | 19 | try: | 21 | try: |
4811 | 20 | from bzrlib._bencode_pyx import bdecode, bdecode_as_tuple, bencode, Bencached | 22 | from bzrlib._bencode_pyx import bdecode, bdecode_as_tuple, bencode, Bencached |
4813 | 21 | except ImportError: | 23 | except ImportError, e: |
4814 | 24 | osutils.failed_to_load_extension(e) | ||
4815 | 22 | from bzrlib.util._bencode_py import bdecode, bdecode_as_tuple, bencode, Bencached | 25 | from bzrlib.util._bencode_py import bdecode, bdecode_as_tuple, bencode, Bencached |
4816 | 23 | 26 | ||
4817 | === modified file 'bzrlib/branch.py' | |||
4818 | --- bzrlib/branch.py 2009-11-23 07:10:47 +0000 | |||
4819 | +++ bzrlib/branch.py 2010-02-18 00:00:52 +0000 | |||
4820 | @@ -1,4 +1,4 @@ | |||
4822 | 1 | # Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd | 1 | # Copyright (C) 2005-2010 Canonical Ltd |
4823 | 2 | # | 2 | # |
4824 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
4825 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
4826 | @@ -46,9 +46,10 @@ | |||
4827 | 46 | ) | 46 | ) |
4828 | 47 | """) | 47 | """) |
4829 | 48 | 48 | ||
4831 | 49 | from bzrlib.decorators import needs_read_lock, needs_write_lock | 49 | from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises |
4832 | 50 | from bzrlib.hooks import HookPoint, Hooks | 50 | from bzrlib.hooks import HookPoint, Hooks |
4833 | 51 | from bzrlib.inter import InterObject | 51 | from bzrlib.inter import InterObject |
4834 | 52 | from bzrlib.lock import _RelockDebugMixin | ||
4835 | 52 | from bzrlib import registry | 53 | from bzrlib import registry |
4836 | 53 | from bzrlib.symbol_versioning import ( | 54 | from bzrlib.symbol_versioning import ( |
4837 | 54 | deprecated_in, | 55 | deprecated_in, |
4838 | @@ -502,12 +503,25 @@ | |||
4839 | 502 | left_parent = stop_rev.parent_ids[0] | 503 | left_parent = stop_rev.parent_ids[0] |
4840 | 503 | else: | 504 | else: |
4841 | 504 | left_parent = _mod_revision.NULL_REVISION | 505 | left_parent = _mod_revision.NULL_REVISION |
4842 | 506 | # left_parent is the actual revision we want to stop logging at, | ||
4843 | 507 | # since we want to show the merged revisions after the stop_rev too | ||
4844 | 508 | reached_stop_revision_id = False | ||
4845 | 509 | revision_id_whitelist = [] | ||
4846 | 505 | for node in rev_iter: | 510 | for node in rev_iter: |
4847 | 506 | rev_id = node.key[-1] | 511 | rev_id = node.key[-1] |
4848 | 507 | if rev_id == left_parent: | 512 | if rev_id == left_parent: |
4849 | 513 | # reached the left parent after the stop_revision | ||
4850 | 508 | return | 514 | return |
4852 | 509 | yield (rev_id, node.merge_depth, node.revno, | 515 | if (not reached_stop_revision_id or |
4853 | 516 | rev_id in revision_id_whitelist): | ||
4854 | 517 | yield (rev_id, node.merge_depth, node.revno, | ||
4855 | 510 | node.end_of_merge) | 518 | node.end_of_merge) |
4856 | 519 | if reached_stop_revision_id or rev_id == stop_revision_id: | ||
4857 | 520 | # only do the merged revs of rev_id from now on | ||
4858 | 521 | rev = self.repository.get_revision(rev_id) | ||
4859 | 522 | if rev.parent_ids: | ||
4860 | 523 | reached_stop_revision_id = True | ||
4861 | 524 | revision_id_whitelist.extend(rev.parent_ids) | ||
4862 | 511 | else: | 525 | else: |
4863 | 512 | raise ValueError('invalid stop_rule %r' % stop_rule) | 526 | raise ValueError('invalid stop_rule %r' % stop_rule) |
4864 | 513 | 527 | ||
4865 | @@ -1089,15 +1103,7 @@ | |||
4866 | 1089 | params = ChangeBranchTipParams( | 1103 | params = ChangeBranchTipParams( |
4867 | 1090 | self, old_revno, new_revno, old_revid, new_revid) | 1104 | self, old_revno, new_revno, old_revid, new_revid) |
4868 | 1091 | for hook in hooks: | 1105 | for hook in hooks: |
4878 | 1092 | try: | 1106 | hook(params) |
4870 | 1093 | hook(params) | ||
4871 | 1094 | except errors.TipChangeRejected: | ||
4872 | 1095 | raise | ||
4873 | 1096 | except Exception: | ||
4874 | 1097 | exc_info = sys.exc_info() | ||
4875 | 1098 | hook_name = Branch.hooks.get_hook_name(hook) | ||
4876 | 1099 | raise errors.HookFailed( | ||
4877 | 1100 | 'pre_change_branch_tip', hook_name, exc_info) | ||
4879 | 1101 | 1107 | ||
4880 | 1102 | @needs_write_lock | 1108 | @needs_write_lock |
4881 | 1103 | def update(self): | 1109 | def update(self): |
4882 | @@ -1432,10 +1438,10 @@ | |||
4883 | 1432 | """Return the format for the branch object in a_bzrdir.""" | 1438 | """Return the format for the branch object in a_bzrdir.""" |
4884 | 1433 | try: | 1439 | try: |
4885 | 1434 | transport = a_bzrdir.get_branch_transport(None) | 1440 | transport = a_bzrdir.get_branch_transport(None) |
4887 | 1435 | format_string = transport.get("format").read() | 1441 | format_string = transport.get_bytes("format") |
4888 | 1436 | return klass._formats[format_string] | 1442 | return klass._formats[format_string] |
4889 | 1437 | except errors.NoSuchFile: | 1443 | except errors.NoSuchFile: |
4891 | 1438 | raise errors.NotBranchError(path=transport.base) | 1444 | raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir) |
4892 | 1439 | except KeyError: | 1445 | except KeyError: |
4893 | 1440 | raise errors.UnknownFormatError(format=format_string, kind='branch') | 1446 | raise errors.UnknownFormatError(format=format_string, kind='branch') |
4894 | 1441 | 1447 | ||
4895 | @@ -1790,7 +1796,7 @@ | |||
4896 | 1790 | _repository=a_bzrdir.find_repository(), | 1796 | _repository=a_bzrdir.find_repository(), |
4897 | 1791 | ignore_fallbacks=ignore_fallbacks) | 1797 | ignore_fallbacks=ignore_fallbacks) |
4898 | 1792 | except errors.NoSuchFile: | 1798 | except errors.NoSuchFile: |
4900 | 1793 | raise errors.NotBranchError(path=transport.base) | 1799 | raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir) |
4901 | 1794 | 1800 | ||
4902 | 1795 | def __init__(self): | 1801 | def __init__(self): |
4903 | 1796 | super(BranchFormatMetadir, self).__init__() | 1802 | super(BranchFormatMetadir, self).__init__() |
4904 | @@ -1971,7 +1977,7 @@ | |||
4905 | 1971 | def get_reference(self, a_bzrdir): | 1977 | def get_reference(self, a_bzrdir): |
4906 | 1972 | """See BranchFormat.get_reference().""" | 1978 | """See BranchFormat.get_reference().""" |
4907 | 1973 | transport = a_bzrdir.get_branch_transport(None) | 1979 | transport = a_bzrdir.get_branch_transport(None) |
4909 | 1974 | return transport.get('location').read() | 1980 | return transport.get_bytes('location') |
4910 | 1975 | 1981 | ||
4911 | 1976 | def set_reference(self, a_bzrdir, to_branch): | 1982 | def set_reference(self, a_bzrdir, to_branch): |
4912 | 1977 | """See BranchFormat.set_reference().""" | 1983 | """See BranchFormat.set_reference().""" |
4913 | @@ -2072,7 +2078,7 @@ | |||
4914 | 2072 | _legacy_formats[0].network_name(), _legacy_formats[0].__class__) | 2078 | _legacy_formats[0].network_name(), _legacy_formats[0].__class__) |
4915 | 2073 | 2079 | ||
4916 | 2074 | 2080 | ||
4918 | 2075 | class BzrBranch(Branch): | 2081 | class BzrBranch(Branch, _RelockDebugMixin): |
4919 | 2076 | """A branch stored in the actual filesystem. | 2082 | """A branch stored in the actual filesystem. |
4920 | 2077 | 2083 | ||
4921 | 2078 | Note that it's "local" in the context of the filesystem; it doesn't | 2084 | Note that it's "local" in the context of the filesystem; it doesn't |
4922 | @@ -2124,9 +2130,12 @@ | |||
4923 | 2124 | return self.control_files.is_locked() | 2130 | return self.control_files.is_locked() |
4924 | 2125 | 2131 | ||
4925 | 2126 | def lock_write(self, token=None): | 2132 | def lock_write(self, token=None): |
4926 | 2133 | if not self.is_locked(): | ||
4927 | 2134 | self._note_lock('w') | ||
4928 | 2127 | # All-in-one needs to always unlock/lock. | 2135 | # All-in-one needs to always unlock/lock. |
4929 | 2128 | repo_control = getattr(self.repository, 'control_files', None) | 2136 | repo_control = getattr(self.repository, 'control_files', None) |
4930 | 2129 | if self.control_files == repo_control or not self.is_locked(): | 2137 | if self.control_files == repo_control or not self.is_locked(): |
4931 | 2138 | self.repository._warn_if_deprecated(self) | ||
4932 | 2130 | self.repository.lock_write() | 2139 | self.repository.lock_write() |
4933 | 2131 | took_lock = True | 2140 | took_lock = True |
4934 | 2132 | else: | 2141 | else: |
4935 | @@ -2139,9 +2148,12 @@ | |||
4936 | 2139 | raise | 2148 | raise |
4937 | 2140 | 2149 | ||
4938 | 2141 | def lock_read(self): | 2150 | def lock_read(self): |
4939 | 2151 | if not self.is_locked(): | ||
4940 | 2152 | self._note_lock('r') | ||
4941 | 2142 | # All-in-one needs to always unlock/lock. | 2153 | # All-in-one needs to always unlock/lock. |
4942 | 2143 | repo_control = getattr(self.repository, 'control_files', None) | 2154 | repo_control = getattr(self.repository, 'control_files', None) |
4943 | 2144 | if self.control_files == repo_control or not self.is_locked(): | 2155 | if self.control_files == repo_control or not self.is_locked(): |
4944 | 2156 | self.repository._warn_if_deprecated(self) | ||
4945 | 2145 | self.repository.lock_read() | 2157 | self.repository.lock_read() |
4946 | 2146 | took_lock = True | 2158 | took_lock = True |
4947 | 2147 | else: | 2159 | else: |
4948 | @@ -2153,6 +2165,7 @@ | |||
4949 | 2153 | self.repository.unlock() | 2165 | self.repository.unlock() |
4950 | 2154 | raise | 2166 | raise |
4951 | 2155 | 2167 | ||
4952 | 2168 | @only_raises(errors.LockNotHeld, errors.LockBroken) | ||
4953 | 2156 | def unlock(self): | 2169 | def unlock(self): |
4954 | 2157 | try: | 2170 | try: |
4955 | 2158 | self.control_files.unlock() | 2171 | self.control_files.unlock() |
4956 | 2159 | 2172 | ||
4957 | === modified file 'bzrlib/btree_index.py' | |||
4958 | --- bzrlib/btree_index.py 2009-09-09 19:32:27 +0000 | |||
4959 | +++ bzrlib/btree_index.py 2010-02-18 00:00:50 +0000 | |||
4960 | @@ -1,4 +1,4 @@ | |||
4962 | 1 | # Copyright (C) 2008 Canonical Ltd | 1 | # Copyright (C) 2008, 2009, 2010 Canonical Ltd |
4963 | 2 | # | 2 | # |
4964 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
4965 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
4966 | @@ -17,6 +17,7 @@ | |||
4967 | 17 | 17 | ||
4968 | 18 | """B+Tree indices""" | 18 | """B+Tree indices""" |
4969 | 19 | 19 | ||
4970 | 20 | import cStringIO | ||
4971 | 20 | from bisect import bisect_right | 21 | from bisect import bisect_right |
4972 | 21 | import math | 22 | import math |
4973 | 22 | import tempfile | 23 | import tempfile |
4974 | @@ -30,6 +31,7 @@ | |||
4975 | 30 | index, | 31 | index, |
4976 | 31 | lru_cache, | 32 | lru_cache, |
4977 | 32 | osutils, | 33 | osutils, |
4978 | 34 | static_tuple, | ||
4979 | 33 | trace, | 35 | trace, |
4980 | 34 | ) | 36 | ) |
4981 | 35 | from bzrlib.index import _OPTION_NODE_REFS, _OPTION_KEY_ELEMENTS, _OPTION_LEN | 37 | from bzrlib.index import _OPTION_NODE_REFS, _OPTION_KEY_ELEMENTS, _OPTION_LEN |
4982 | @@ -60,14 +62,20 @@ | |||
4983 | 60 | def __init__(self): | 62 | def __init__(self): |
4984 | 61 | """Create a _BuilderRow.""" | 63 | """Create a _BuilderRow.""" |
4985 | 62 | self.nodes = 0 | 64 | self.nodes = 0 |
4987 | 63 | self.spool = tempfile.TemporaryFile() | 65 | self.spool = None# tempfile.TemporaryFile(prefix='bzr-index-row-') |
4988 | 64 | self.writer = None | 66 | self.writer = None |
4989 | 65 | 67 | ||
4990 | 66 | def finish_node(self, pad=True): | 68 | def finish_node(self, pad=True): |
4991 | 67 | byte_lines, _, padding = self.writer.finish() | 69 | byte_lines, _, padding = self.writer.finish() |
4992 | 68 | if self.nodes == 0: | 70 | if self.nodes == 0: |
4993 | 71 | self.spool = cStringIO.StringIO() | ||
4994 | 69 | # padded note: | 72 | # padded note: |
4995 | 70 | self.spool.write("\x00" * _RESERVED_HEADER_BYTES) | 73 | self.spool.write("\x00" * _RESERVED_HEADER_BYTES) |
4996 | 74 | elif self.nodes == 1: | ||
4997 | 75 | # We got bigger than 1 node, switch to a temp file | ||
4998 | 76 | spool = tempfile.TemporaryFile(prefix='bzr-index-row-') | ||
4999 | 77 | spool.write(self.spool.getvalue()) | ||
5000 | 78 | self.spool = spool |
Hi all,
This branch fixes merge so that it works when the this_tree is not a eMerger was using merger. this_tree. branch, to
working tree. The ConfigurableFil
retrieve the configuration, but it should actually use merger.this_branch.
The test simply shows that generating a preview transform with a RevisionTree
as the this_tree does not raise an exception.