Commit Graph

193 Commits

Author SHA1 Message Date
Jun Wu
fb56b1962d dag: move optimization hints to a dedicate structure
Summary:
Previously, the NameSet has properties like "is_all", "is_topo_sorted", etc.
To make lazy sets efficient, it's important to have hints about min / max Ids
and maybe some other information.

Add a dedicated Hints structure for that.

Reviewed By: sfilipco

Differential Revision: D21626219

fbshipit-source-id: 845e88d3333f0f48f60f2739adae3dccc4a2dfc4
2020-06-02 14:00:36 -07:00
Jun Wu
48c003fb11 revlogindex: impl IdConvert and PrefixLookup for RevlogIndex
Summary:
Implements part of the dag IdMap related traits.

It does not get used yet, but eventually I'd like `pydag` to be able to work
with an abstracted dag including RevlogIndex.

Reviewed By: sfilipco

Differential Revision: D21626210

fbshipit-source-id: 53f19622f03fd71b76073dccf8dcc9b4778b40ca
2020-06-02 14:00:35 -07:00
Jun Wu
38d6c6a819 revlogindex: include NodeRevMap in RevlogIndex
Summary:
This will allow RevLogIndex to answer node -> rev and hex lookup queries.

Also change RevlogIndex::new to take file names so it can write back the
nodemap index when the index is lagging. That part of logic currently exists in
pyindexes + clindex.pyx, which are going to be replaced by revlogindex.

Practically, this will generate a `00changelog.nodemap` file in svfs, which is
temporarily unused, but will be used once clindex.pyx gets replaced.

Reviewed By: sfilipco

Differential Revision: D21626209

fbshipit-source-id: 297d9eff26a73c26558708f7a2290d4d8ba1e777
2020-06-02 14:00:34 -07:00
Jun Wu
6b595410ce revlogindex: make changelog data type consistent
Summary:
Change `NodeRevMap`'s changelog type from `[u8]` to `[RevlogEntry]`.
This makes it consistent with `RevlogIndex`.

Reviewed By: sfilipco

Differential Revision: D21626203

fbshipit-source-id: 7457f48ccd7b3489264684a5db21d21e9eb7a937
2020-06-01 10:56:55 -07:00
Jun Wu
445e9f9fa7 pyindexes: move NodeRevMap to revlogindex
Summary:
NodeRevMap helps converting from a commit hash to a rev number. It's similar to
IdMap in the dag crate, but was designed for the revlog.

Move NodeRevMap to revlogindex so it becomes easier to implement the IdConvert
trait required by the dag crate.

Reviewed By: sfilipco

Differential Revision: D21626211

fbshipit-source-id: 14996f1234231b507efb5186ec30f84df5aaad10
2020-06-01 10:56:55 -07:00
Jun Wu
40fbbff9af pyrevlogindex: move non-Python logic to a pure Rust crate
Summary:
The idea is that the pure Rust revlogindex crate can implement the DagAlgorithm
interface so we will have a consistent interface in the code base that works
for both the existing storage (revlog) and the new segmented changelog.

The other way to do this is to implement the `bindings.dag.namedag` interface
in pure Python for the revlog-based DAG, or supporting quite different
interfaces (ex. revlog DAG and the Rust segmented changelog DAG) in the code
base. At present, I think implementing the Rust DAG traits for revlog is the
most appealing, partially because we already have some key algorithms
implemented (ex. prefix lookup, common ancestors, etc).

Reviewed By: sfilipco

Differential Revision: D21626197

fbshipit-source-id: 733b1af1bcd5fc0784764fc7103412988894d43b
2020-06-01 10:56:54 -07:00
Durham Goode
995a2852c1 configs: return bytes for config parsers validation results
Summary:
Previously the return type was String which, in Python 2, could turn into bytes or
unicode depending on the contents of the string. We always want bytes in Python
2, so let's use the Str type instead.

Reviewed By: quark-zju

Differential Revision: D21794189

fbshipit-source-id: 6493fbacab354a78476f522fc3c41b7336dbbdb1
2020-06-01 09:45:19 -07:00
Jun Wu
64dc05ab9d dag: move add_heads, flush, add_heads_and_flush to traits
Summary: This allows other kinds of DAG to implement the operations.

Reviewed By: sfilipco

Differential Revision: D21626220

fbshipit-source-id: 896c5ccebb1672324d346dfca6bcac9b4d3b4929
2020-05-27 12:16:47 -07:00
Jun Wu
4934987796 dag: implement PrefixLookup for Dag, MemDag and MemIdMap
Summary: This makes things a bit more flexible.

Reviewed By: sfilipco

Differential Revision: D21626194

fbshipit-source-id: f3ad486bcd5a6478d9e00f674d48f99504cded8c
2020-05-27 12:16:46 -07:00
Jun Wu
26217dcdb5 dag: move hex prefix lookup to a trait
Summary: This makes it possible for other types to implement the hex prefix lookup.

Reviewed By: sfilipco

Differential Revision: D21626218

fbshipit-source-id: 96e8b8c37e5aae2bd60658a238333b61902936d1
2020-05-27 12:16:46 -07:00
Jun Wu
38cc83e1bf dag: add short aliases for main public types
Summary:
Types like IdDag are not really used. The use of the word "name" is sometimes
confusing in other context. Therefore export shorter names like Dag, MemDag,
Vertex, avoid "name" in NameDag, MemNameDag and NameSet. This makes external
code shorter and less ambiguous.

Reviewed By: sfilipco

Differential Revision: D21626212

fbshipit-source-id: 5bcf3cecfd38277149b41bf3ba9e6d4ef2a07b2b
2020-05-27 12:16:45 -07:00
Jun Wu
e0d11803f2 dag: move DagAlgorithm to an independent trait
Summary:
This decouples DagAlgorithm from the IdMap + IdDag backend, making it possible
to support other kinds of backends of DagAlgorithm (ex. a revlog backend).

Reviewed By: sfilipco

Differential Revision: D21626200

fbshipit-source-id: f53cc271a200062e9c02f739b6453e1d7de84e6d
2020-05-27 12:16:45 -07:00
Durham Goode
8ed66bc3fc pyrevisionstore: fix unused code warnings
Summary:
When we got rid of the delta logic, we also needed to get rid of some
unused functions.

Reviewed By: singhsrb

Differential Revision: D21725043

fbshipit-source-id: ac069e6b0468e2275f353a9970b8971b5a2cfa23
2020-05-26 18:09:22 -07:00
Durham Goode
9f6f200a08 configs: version dynamic configs
Summary:
If we release a new version of Mercurial, we want to ensure that it's
builtin configs are used immediately. To do so, let's write a version number
into the generated config file, and if the version number doesn't match, we
force a synchronous regeneration of the config file.

For now, if regeneration fails, we just log it. In the future we'll probably
throw an exception and block the user since we want to ensure people are running
with modern configuration.

Reviewed By: quark-zju

Differential Revision: D21651317

fbshipit-source-id: 3edbaf6777f4ca2363d8617fad03c21204b468a2
2020-05-20 13:35:28 -07:00
Durham Goode
f0d7044aff configs: apply dynamicconfig during clone
Summary:
During clone the hgrc.dynamic file doesn't exist and doesn't even have
a place for us to generate it to. Let's instead generate and apply the config in
memory.

In the future, if generate fetches data from the network, this will mean clone
would depend on the network, since if generate fails the clone would fail. In
some situations this is desirable, since users shouldn't be cloning without our
approved configs, but if it causes problems we could probably tweak generate to
support an offline mode.

Reviewed By: quark-zju

Differential Revision: D21643086

fbshipit-source-id: d9a758207738d5983213d95725061517e0aa17db
2020-05-19 19:51:27 -07:00
Durham Goode
861f813f25 configs: convert facebook_overrides.rc
Summary: Converts facebook_overrides.rs to our dynamic config generator

Reviewed By: quark-zju

Differential Revision: D21625721

fbshipit-source-id: 2a374939d90f1fb7f9173268e2a7fa636d672393
2020-05-19 13:23:19 -07:00
Jun Wu
a27bf2fc42 pyrenderdag: support non-revision-numbers graph vertexes
Summary:
Change pyrenderdag to accept non-revision-number graph vertexes so it can
render a graph even if the graph does not use revision numbers.

The next diff wants this behavior so it can just emit commit hashes to
the renderer without caring about revision numbers. The type is made
so it can still support revision numbers, since the legacy graphlog
interface would still use revision numbers.

Reviewed By: markbt

Differential Revision: D21554671

fbshipit-source-id: 20572683b831f7cecb03957c83f278ff3903eff0
2020-05-14 12:03:44 -07:00
Jun Wu
96ac755c06 pydag: fix lazy set iteration
Summary:
The previous code was wrong - it converts the PyObject to iterator every time
(ex. if the PyObject is a set, then it calls `set.__iter__` every time, and
will only get the first element of the set).

For example, it will enter an infinite loop for evaluating this:

  bindings.dag.nameset({'1', '2'})

Fix it by calling `__iter__`, to get the iterator object and use that instead
of the original PyObject.

Reviewed By: markbt

Differential Revision: D21554676

fbshipit-source-id: 0f2adae8f123530cee2d473da37ca1a93a941fde
2020-05-14 12:03:44 -07:00
Jun Wu
aeac1551d2 dag: implement beautify
Summary:
This function reorders commits so the graph looks better.
It will be used to optimize graph rendering for cloud smartlog (and perhaps
smartlog in the future).

Reviewed By: markbt

Differential Revision: D21554675

fbshipit-source-id: d3f0f27c7935c49581cfa6e87d7c32eb5a075f75
2020-05-14 12:03:43 -07:00
Jun Wu
0ac5c6d4f3 pymutationstore: expose the getdag API
Summary: Expose the API that returns a real graph.

Reviewed By: DurhamG

Differential Revision: D21486520

fbshipit-source-id: 4ebdb4011df8971c54930173c4e77503cd2dac47
2020-05-13 09:45:24 -07:00
Jun Wu
e817197b09 bindings: add bindings to regex
Summary:
This allows us to replace the pyre2 C++ bindings so the fast regex engine can
work with Python 3, and simplify our build steps.

Reviewed By: DurhamG

Differential Revision: D20973179

fbshipit-source-id: e123ac18954991f2c701526108f5c2ecd2b31a3b
2020-05-12 16:32:50 -07:00
Ellis Hoag
1d0d626a36 Pass config object down to repack
Summary:
Pass `configparser::config::ConfigSet` to `repack` in
`revisionstore/src/repack.rs` so that we can use various config values in `filter_incrementalpacks`.

* `repack.maxdatapacksize`, `repack.maxhistpacksize`
  * The overall max pack size
* `repack.sizelimit`
  * The size limit for any individual pack
* `repack.maxpacks`
  * The maximum number of packs we want to have after repack (overrides sizelimit)

Reviewed By: xavierd

Differential Revision: D21484836

fbshipit-source-id: 0407d50dfd69f23694fb736e729819b7285f480f
2020-05-11 16:41:30 -07:00
Xavier Deguillard
2001c3fd69 revisionstore: add translate_lfs_missing to remote store get
Summary:
When Qing implemented all the get method, the translate_lfs_missing function
didn't exist, and I forgot to add them in the right places when landing the
diff that added it. Fix this.

Reviewed By: sfilipco

Differential Revision: D21418043

fbshipit-source-id: baf67b0fe60ed20aeb2c1acd50a209d04dc91c5e
2020-05-11 10:34:01 -07:00
Jun Wu
d8abb30eeb pydag: expose some memnamedag APIs
Summary: Make them reusable in other Python bindings, ex. pymutation.

Reviewed By: sfilipco

Differential Revision: D21486524

fbshipit-source-id: 258455c6a442353c77588fadcb560cb5a170926e
2020-05-11 09:50:01 -07:00
Jun Wu
6835eb4b9d pydag: expose render into string feature for memnamedag
Summary: This makes it easier to visualize a MemNameDag.

Reviewed By: sfilipco

Differential Revision: D21486523

fbshipit-source-id: c65f1fc421bd654dc820faae3c93f2aa57f910d4
2020-05-11 09:50:01 -07:00
Jun Wu
010bcac66a pydag: expose MemNameDag APIs
Summary:
This will allow clients to operate on MemNameDag.

Unfortunately, it isn't that easy to reuse code in `py_class!`. Since they are
just thin wrappers, I live with the copy-paste for now.

Reviewed By: sfilipco

Differential Revision: D21479015

fbshipit-source-id: ddcc7f5c7ede6bb1e9c73d058779805875b09200
2020-05-11 09:50:01 -07:00
Jun Wu
f014f86b7a dag: move NameDag algorithms to a trait
Summary:
This makes it easier to add an "in-memory-only" NameDag with all the algorithms
implemented.

Reviewed By: sfilipco

Differential Revision: D21479020

fbshipit-source-id: c1a73e95f3291c273c800650f70db2a7eb0966d7
2020-05-11 09:49:56 -07:00
Meyer Jacobs
d49ac73f4c datastore: remove HgIdDataStore ::get_delta and ::get_delta_chain
Summary:
Remove HgIdDataStore::get_delta and all implementations. Remove HgIdDataStore::get_delta_chain from trait, remove all unnecessary implentations, remove all implementations from public Rust API. Leave Python API and introduce "delta-wrapping".

MutableDataPack::get_delta_chain must remain in some form, as it necessary to implement get using a sequence of Deltas. It has been moved to a private inherent impl.

DataPack::get_delta_chain must remain in some form for the same reasons, and in fact both implenetations can probably be merged, but it is also used in repack.rs for the free function repack_datapack. There are a few ways to address this without making DataPack::get_delta_chain part of the public API. I've currently chosen to make the method pub(crate), ie visible only within the revisionstore crate. Alternatively, we could move the repack_datapack function to a method on DataPack, or use a trait in a private module, or some other technique to restrict visibility to only where necessary.

UnionDataStore::get has been modified to call get on it's sub-stores and return the first which matches the given key.

MultiplexDeltaStore has been modified to implement get similarly to UnionDataStore.

Reviewed By: xavierd

Differential Revision: D21356420

fbshipit-source-id: d04e18a0781374a138395d1c21c3687897223d15
2020-05-07 11:04:01 -07:00
Jun Wu
44c8c7a9e3 transaction: write hgrc to metalog
Summary:
This allows us to understand what config is used during a transaction.
For example, is `selectivepull` enabled during a `pull`?

Reviewed By: DurhamG

Differential Revision: D21222146

fbshipit-source-id: a8c82f2b02e9657885947a706f728e28b1bfc1e2
2020-05-06 12:15:36 -07:00
Durham Goode
e67d609e1d configs: validate dynamic configs
Summary:
Adds python logic for validating the dynamic configs. Any dynamic
configs that don't match the given list of rc files will be reported and removed

Reviewed By: quark-zju

Differential Revision: D21310919

fbshipit-source-id: 07f584bba990f1b01347dfbc285e3ca814fe5c5a
2020-05-05 18:19:09 -07:00
Jun Wu
5b881f086f pyzstore: further reduce cpython_ext::Bytes usage
Summary: This avoids data copies.

Reviewed By: DurhamG

Differential Revision: D21213075

fbshipit-source-id: 9575173f163d71543affabd9861931c11086f40a
2020-05-01 14:24:52 -07:00
Jun Wu
73ff6559e6 zstore: add simple caching
Summary: Add simple caching so zstore can avoid some zstd calculation.

Reviewed By: DurhamG

Differential Revision: D21213076

fbshipit-source-id: 5e3152949cf4e6d6193c3ef3401f24e2efac5620
2020-05-01 14:24:52 -07:00
Carolyn Busch
4eeab3b81b Update cpython to 0.5
Summary:
D21270958 updated the cpython, python27-sys, and python3-sys crates to 0.5. Update
the Mercurial cargo dependencies to match.

Reviewed By: xavierd

Differential Revision: D21281875

fbshipit-source-id: ccad68749a25d11240351b5faeef27cb9c693456
2020-04-28 11:47:41 -07:00
Jun Wu
d479053954 metalog: support exporting to a git repo
Summary:
I wanted to figure out "who added this visible head", "what is the difference
between this metalog root and that root". Those are actually source control
operations (blame, diff). Add a git export feature so we can export metalog
to git to run those queries.

Choosing git here as we don't have native Rust utilities to create a more
efficient hg repo yet.

Ideally we can also make hg operate on a metalog directory as a "metalogrepo"
directly. However that seems to be quite difficult right now due to poor
abstractions.

Reviewed By: DurhamG

Differential Revision: D21213073

fbshipit-source-id: 4cc0331fbad6e1586907c0a66c18bcc25608ea49
2020-04-27 20:25:25 -07:00
Jun Wu
3df5fcf779 pymetalog: add handy APIs for debugshell
Summary:
This makes metalog easier to use in debugshell context. For example, to
investigate the "bookmarks" in the past, the code gets simplified from:

  roots = b.metalog.metalog.listroots(repo.svfs.join('metalog'))
  past_ml = b.metalog.metalog(repo.svfs.join('metalog'), root[10])
  past_ml.get("bookmarks")

to:

  roots = ml.roots()
  past_ml = ml.checkout(roots[10])
  past_ml.get("bookmarks")

Reviewed By: DurhamG

Differential Revision: D21162568

fbshipit-source-id: 7cc5581afe596a3d2696311a36ac11caa718428a
2020-04-27 20:04:18 -07:00
Jun Wu
a0207c4542 metalog: expose root id API
Summary: This allows the Python world to obtain the root ID for logging purpose.

Reviewed By: DurhamG

Differential Revision: D21179513

fbshipit-source-id: 3f289c06d3d470ff492de39fa985203b3facbf00
2020-04-27 19:50:58 -07:00
Xavier Deguillard
86965b2f80 revisionstore: query store before fetching
Summary:
While the change looks fairly mechanical and simple, the why is a bit tricky.
If we follow the calls of `ContentStore::get`, we can see that it first goes
through every on-disk stores, and then switches to the remote ones, thanks to
that, when we reach the remote stores there is no reason to believe that the
local store attached to them contains the data we're fetching. Thus the
code used to always prefetch the data, before reading from the store what was
just written.

While this is true for regular stores (packstore, indexedlog, etc), it starts
to break down for the LFS store. The reason being that the LFS store is
internally represented as 2 halves: a pointer store, and a blob store.  It is
entirely possible that the LFS store contains a pointer, but not the actual
blob. In that case, the `get` executed on the LFS store will simply return
`Ok(None)` as the blob just isn't present, which will cause us to fallback to
the remote stores. Since we do have the pointer locally, we shouldn't try to
refetch it from the remote store, and thus why a `get_missing` needs to be run
before fetching from the remote store.

As I was writing this, I realized that all of this subtle behavior is basically
the same between all the stores, but unfortunately, doing a:
  impl<T: RemoteDataStore + ?Sized> HgIdDataStore for T
Conflicts with the one for `Deref<Target=HgIdDataStore>`. Macros could be used
to avoid code duplication, but for now let's not stray into them.

Reviewed By: DurhamG

Differential Revision: D21132667

fbshipit-source-id: 67a2544c36c2979dbac70dac5c1d055845509746
2020-04-27 12:53:11 -07:00
Durham Goode
344837cca4 filesystem: python bindings for new rust pending changes
Summary:
Adds initial python bindings for the rust pending changes. This is not
ready for production usage yet, but having the bindings let's me test changes
more easily until we're ready for automated tests.

Reviewed By: xavierd

Differential Revision: D20546896

fbshipit-source-id: c0ad7155e5068f45bf9c987030746e6c5f35c26a
2020-04-24 13:58:53 -07:00
Mark Thomas
c05efd8a5c mutationstore: move MutationEntry type to types crate
Summary: Move the MutationEntry type to the Mercurial types crate.  This will allow us to use it from Mononoke.

Reviewed By: quark-zju

Differential Revision: D20871338

fbshipit-source-id: 8de3bb8a2673673bc4c8a6cc7578a0a76358c14a
2020-04-23 08:58:10 -07:00
Durham Goode
e97d8d8895 vfs: move vfs logic into its own crate
Summary:
This logic will be used in a variety of places (update workers, status,
etc). Let's move it somewhere common.

Reviewed By: xavierd

Differential Revision: D20771623

fbshipit-source-id: b4de7c1d20055a10bbc1143d44c55ea1045ec62a
2020-04-22 19:55:49 -07:00
Durham Goode
c1b8f86359 treestate: store TreeState in an Arc<Mutex<_>>
Summary:
In a later diff we'll need to be able to hand a reference to the
TreeState to the pending changes iterator. We'd like to be able to hand a
Rc<RefCell<TreeState>> but cpython requires that its fields implement Send. The
simplest solution is to use Arc<Mutex<_>>. Once we finish Rustifying all of this
code we can drop the cpython requirement that this work across threads and
downgrade this to a Rc<RefCell<_>>.

Reviewed By: xavierd

Differential Revision: D20546904

fbshipit-source-id: a4a1ce6973f53b3bb95f227616149f98fcd780e0
2020-04-22 19:55:49 -07:00
Durham Goode
a6e2b90c2e pathauditor: move into workingcopy crate
Summary:
PathAuditor will be needed for native status soon. Let's move it into
the workingcopy crate.

Reviewed By: xavierd

Differential Revision: D20546906

fbshipit-source-id: ef69f88ee828a72e82b5e944cc7913f391bd8a2f
2020-04-22 19:55:49 -07:00
Durham Goode
f764f12f72 tracing: fix function tracing
Summary:
The old pytracing logic walked the stack looking for the most recent
spanid. This was fragile and missed a bunch of spots because the function name
wasn't present in f_globals. Let's make this explicit by tracking the stack of
spanids for each python thread.

Reviewed By: quark-zju

Differential Revision: D21068332

fbshipit-source-id: 98759640fa1081bc5bc0805cc620e35a2de9dae3
2020-04-21 13:23:50 -07:00
Xavier Deguillard
1d231ebac7 revisionstore: return non-uploaded keys
Summary:
Ideally, either the ContentStore, or the upper layer should verify that we
haven't missed uploading a blob, which could lead to weird behavior down the
line. For now, all the stores will return the keys of the blobs that weren't
uploaded, which allows us to return these keys to Python.

Reviewed By: DurhamG

Differential Revision: D21103998

fbshipit-source-id: 5bab0bbec32244291c65a07aa2a13aec344e715e
2020-04-20 21:02:35 -07:00
Durham Goode
7edc29d07c filesystem: move rust walker to it's own file
Summary:
We'll be adding more data to the filesystem layer, so let's move this
out of lib.rs.

Also made a slight tweak to expose File metadata in the walk results, which will be used by the future pending changes logic to avoid re-stating the file.

Reviewed By: xavierd

Differential Revision: D20546903

fbshipit-source-id: 70456055b0da601990e6d6ff535678d2df6c50ba
2020-04-16 16:51:21 -07:00
Jun Wu
c2ffda7622 pager: configure streampager using hg configs
Summary:
This allows the streampager to be configured via hgrc files.

Default are picked so the behavior is closer to the current default pager
(`less -FRX`).

Reviewed By: DurhamG

Differential Revision: D20902034

fbshipit-source-id: 994ab963ceace02eeb1d18cfa5768e411ca3610b
2020-04-15 18:23:11 -07:00
Xavier Deguillard
d2c56495e4 metalog: fix for python3
Summary:
While keys are strings, values are bytes buffer and thus needs to be converted
sometimes.

Reviewed By: DurhamG

Differential Revision: D20974484

fbshipit-source-id: 13394f5dc43191e85e4b1d350cc4fbbd8489572a
2020-04-13 14:55:11 -07:00
Jun Wu
49acaf17b3 bindings: implement diffhelpers
Summary:
The diffhelpers.c lacks of type checks and segfaults on Python 3:

  (gdb) r --config patch.eol=auto import -d '0 0' -m 'test patch.eol' --bypass ../test.diff --debug --traceback
  Program received signal SIGSEGV, Segmentation fault.
  0x00007ffff7263016 in __strcmp_sse42 () from /lib64/libc.so.6
  (gdb) bt
  #0  0x00007ffff7263016 in __strcmp_sse42 () from /lib64/libc.so.6
  #1  0x00007fffee5e3d3b in testhunk (self=<optimized out>, args=<optimized out>) at edenscm/mercurial/cext/diffhelpers.c:151

Looking at the diffhelpers usage, it seems to be using the `bytes` type
(bytes on Python 2, str on Python 3). Let's implement it in Rust so `rust-cpython`
will complain if the type is not `bytes`.

Reviewed By: xavierd

Differential Revision: D20935366

fbshipit-source-id: 8b474555b52caeab4175d7dad44c4c4e7097e557
2020-04-09 18:25:53 -07:00
Xavier Deguillard
0dca734464 revisionstore: add upload to RemoteDataStore
Summary: This method will be used to upload local LFS blobs to the LFS server.

Reviewed By: DurhamG

Differential Revision: D20843137

fbshipit-source-id: 33a331c42687c47442189ee329da33cb5ce4d376
2020-04-07 16:53:34 -07:00
Jun Wu
a4a21b72d1 pager: add bindings to expose Rust's pager features to Python
Summary:
This exposes the Rust's pager to Python. Right now it's using the system
terminal.

Reviewed By: DurhamG

Differential Revision: D20887174

fbshipit-source-id: c72f31a58475e76f8097c515dd29f911d2ac4df1
2020-04-07 15:57:07 -07:00